成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

我寫的代碼,又被CTO罵了......

開發 前端 開發工具
大多數時候我都是寫一些業務代碼,可能一堆 CRUD 就能解決問題,但是這樣的工作對技術人的提升并不多,如何讓自己從業務中解脫出來找到寫代碼的樂趣呢,我做過一些嘗試,使用設計模式改善自己的業務代碼就是其中的一種。

大多數時候我都是寫一些業務代碼,可能一堆 CRUD 就能解決問題,但是這樣的工作對技術人的提升并不多,如何讓自己從業務中解脫出來找到寫代碼的樂趣呢,我做過一些嘗試,使用設計模式改善自己的業務代碼就是其中的一種。

 

[[345303]] 

圖片來自 Pexels

“你這代碼寫的像坨屎”,今天我的代碼又被當作典型被 CTO 罵了......于是他給我的建議如下:

責任鏈設計模式

模式定義

請求在一個鏈條上處理,鏈條上的受理者處理完畢之后決定是繼續往后傳遞還是中斷當前處理流程。

適用場景

適用于多節點的流程處理,每個節點完成各自負責的部分,節點之間不知道彼此的存在,比如 OA 的審批流,Java Web 開發中的 Filter 機制。

舉一個生活中的例子,筆者之前租房的時候遇到了所謂的黑中介,租的時候感覺自己是上帝,但是壞了東西找他修的時候就像個孫子一樣。

中介讓我找門店客服,門店客服又讓我找房東,房東又讓我找她家老公,最終好說歹說才把這事了了(租房一定要找正規中介)。

實踐經驗

筆者目前所做的業務是校園團餐的聚合支付,業務流程很簡單:

  • 學生打開手機付款碼支付。
  • 食堂大媽使用機具掃付款碼收款。

大學食堂有個背景是這樣的,食堂有補貼,菜品比較便宜,所以學校是不愿意讓社會人士去學校食堂消費的,鑒于此,我們在支付之前加了一套是否允許支付的檢驗邏輯。

大體如下:

  • 某檔口只允許某類用戶用戶消費,比如教師檔口只允許教師消費,學生檔口不允許校外用戶消費。
  • 某個檔口一天只允許某類用戶消費幾次,比如教師食堂一天只允許學生消費一次。
  • 是否允許非清真學生消費,比如某些清真餐廳,是不允許非清真學生消費的。

針對這幾類情況我建立了三類過濾器,分別是:

  • SpecificCardUserConsumeLimitFilter:按用戶類型判斷是否允許消費。
  • DayConsumeTimesConsumeLimitFilter:按日消費次數判斷是否允許消費。
  • MuslimConsumeLimitFilter:非清真用戶是否允許消費。

判斷邏輯是先通過 SpecificCardUserConsumeLimitFilter 判斷當前用戶是否可以在此檔口消費。

如果允許繼續由 DayConsumeTimesConsumeLimitFilter 判斷當天消費次數是否已用完;如果未用完繼續由 MuslimConsumeLimitFilter 判斷當前用戶是否滿足清真餐廳的就餐條件,前面三條判斷,只要有一個不滿足就提前返回。

部分代碼如下:

  1. public boolean canConsume(String uid,String shopId,String supplierId){ 
  2.     //獲取用戶信息,用戶信息包含類型(student:學生,teacher:老師,unknown:未知用戶)、名族(han:漢族,mg:蒙古族) 
  3.     UserInfo userInfo = getUserInfo(uid); 
  4.     //獲取消費限制信息,限制信息包含是否允許非清真消費、每種類型的用戶是否允許消費以及允許消費的次數 
  5.    ConsumeConfigInfo consumeConfigInfo = getConsumeConfigInfo(shopId,supplierId)  
  6.  
  7.  
  8.     // 構造消費限制過濾器鏈條 
  9.     ConsumeLimitFilterChain filterChain = new ConsumeLimitFilterChain(); 
  10.     filterChain.addFilter(new SpecificCardUserConsumeLimitFilter()); 
  11.     filterChain.addFilter(new DayConsumeTimesConsumeLimitFilter()); 
  12.     filterChain.addFilter(new MuslimConsumeLimitFilter()); 
  13.     boolean checkResult = filterChain.doFilter(filterChain, schoolMemberInfo, consumeConfigInfo); 
  14.  
  15.     //filterChain.doFilter方法 
  16.    public boolean doFilter(ConsumeLimitFilterChain filterChain,UserInfo userInfo, 
  17.              ConsumeConfigInfo consumeConfigInfo ){ 
  18.         //迭代調用過濾器 
  19.         if(index<filters.size()){ 
  20.             return filters.get(index++).doFilter(filterChain, userInfo, consumeConfigInfo); 
  21.         } 
  22.     } 
  23.  
  24.     //SpecificCardUserConsumeLimitFilter.doFilter方法 
  25.      public boolean doFilter(ConsumeLimitFilterChain filterChain,UserInfo userInfo, 
  26.              ConsumeConfigInfo consumeConfigInfo ){ 
  27.                 //獲取某一類型的消費限制,比如student允許消費,unknown不允許消費 
  28.         CardConsumeConfig cardConsumeConfig = findSuitCardConfig(userInfo, consumeConfigInfo); 
  29.  
  30.         // 判斷當前卡用戶是否允許消費 
  31.         if (consumeCardConfig != null) { 
  32.             if ((!CAN_PAY.equals(cardConsumeConfig .getEnabledPay()))) { 
  33.                 return false
  34.             } 
  35.         } 
  36.  
  37.                 //其余情況,繼續往后傳遞 
  38.             return filterChain.doFilter(filterChain, memberInfo, consumeConfig); 
  39.         } 
  40.  
  41.     //DayConsumeTimesConsumeLimitFilter.doFilter方法 
  42.      public boolean doFilter(ConsumeLimitFilterChain filterChain,UserInfo userInfo, 
  43.              ConsumeConfigInfo consumeConfigInfo ){ 
  44.                 //獲取某一類型的消費限制,比如student可以消費2次 
  45.         CardConsumeConfig cardConsumeConfig = findSuitCardConfig(userInfo, consumeConfigInfo); 
  46.  
  47.                 //獲取當前用戶今天的消費次數 
  48.                 int consumeCnt = getConsumeCnt(userInfo)         
  49.         if(consumeCnt >= cardConsumeConfig.getDayConsumeTimesLimit()){ 
  50.                     return false
  51.                 } 
  52.  
  53.                 //其余情況,繼續往后傳遞 
  54.                 return filterChain.doFilter(filterChain, memberInfo, consumeConfig); 
  55.         } 

總結:將每種限制條件的判斷邏輯封裝到了具體的 Filter 中,如果某種限制條件的邏輯有修改不會影響其他條件,如果需要新加限制條件只需要重新構造一個 Filter 織入到 FilterChain 上即可。

策略設計模式

模式定義

定義一系列的算法,把每一個算法封裝起來,并且使它們可相互替換。

適用場景

主要是為了消除大量的 if else 代碼,將每種判斷背后的算法邏輯提取到具體的策略對象中,當算法邏輯修改時對使用者無感知,只需要修改策略對象內部邏輯即可。

這類策略對象一般都實現了某個共同的接口,可以達到互換的目的。

實踐經驗

筆者之前有個需求是用戶掃碼支付以后向檔口的收銀設備推送一條支付消息,收銀設備收到消息以后會進行語音播報,邏輯很簡單,就是調用推送平臺推送一條消息給設備即可。

但是由于歷史原因,某些設備對接的推送平臺是不一樣的,A 類設備優先使用信鴿推送,如果失敗了需要降級到長輪詢機制,B 類設備直接使用自研的推送平臺即可。

還有個現狀是 A 類和 B 類的消息格式是不一樣的(不同的團隊開發,后期被整合到一起)。

鑒于此,我抽象出 PushStrategy 接口,其具體的實現有 IotPushStrategy 和 XingePushStrategy,分別對應自研推送平臺的推送策略和信鴿平臺的推送策略,使用者時針對不同的設備類型使用不同的推送策略即可。

部分代碼如下:

  1. /** 
  2.  * 推送策略 
  3.  * / 
  4. public interface PushStrategy { 
  5.     /** 
  6.          @param deviceVO設備對象,包扣設備sn,信鴿pushid 
  7.          @param content,推送內容,一般為json 
  8.         */ 
  9.     public CallResult push(AppDeviceVO deviceVO, Object content); 
  10.  
  11. IotPushStrategy implements PushStrategy{ 
  12.         /** 
  13.          @param deviceVO設備對象,包扣設備sn,信鴿pushid 
  14.          @param content,推送內容,一般為json 
  15.         */ 
  16.     public CallResult push(AppDeviceVO deviceVO, Object content){ 
  17.             //創建自研推送平臺需要的推送報文 
  18.             Message message = createPushMsg(deviceVO,content); 
  19.  
  20.             //調用推送平臺推送接口 
  21.             IotMessageService.pushMsg(message); 
  22.         } 
  23.  
  24. XingePushStrategy implements PushStrategy{ 
  25.         /** 
  26.          @param deviceVO設備對象,包扣設備sn,信鴿pushid 
  27.          @param content,推送內容,一般為json 
  28.         */ 
  29.     public CallResult push(AppDeviceVO deviceVO, Object content){ 
  30.             //創建信鴿平臺需要的推送報文 
  31.             JSONObject jsonObject = createPushMsg(content); 
  32.  
  33.             //調用推送平臺推送接口 
  34.             if(!XinggePush.pushMsg(message)){ 
  35.                 //降級到長輪詢 
  36.                 ... 
  37.             } 
  38.         } 
  39.  
  40. /** 
  41. 消息推送Service 
  42. */ 
  43. MessagePushService{ 
  44.     pushMsg(AppDeviceVO deviceVO, Object content){ 
  45.         if(A設備){ 
  46.             XingePushStrategy.push(deviceVO,content); 
  47.         } else if(B設備){ 
  48.             IotPushStrategy.push(deviceVO,content); 
  49.         } 
  50.     } 

總結:將每種通道的推送邏輯封裝到了具體的策略中,某種策略的變更不會影響其他策略,由于實現了共同接口,所以策略可以互相替換,對使用者友好。

比如 Java ThreadPoolExecutor 中的任務拒絕策略,當線程池已經飽和的時候會執行拒絕策略,具體的拒絕邏輯被封裝到了 RejectedExecutionHandler 的 rejectedExecution 中。

模板設計模式

模式定義

模板的價值就在于骨架的定義,骨架內部將問題處理的流程已經定義好,通用的處理邏輯一般由父類實現,個性化的處理邏輯由子類實現。

比如炒土豆絲和炒麻婆豆腐,大體邏輯都是:

  • 切菜
  • 放油
  • 炒菜
  • 出鍋

1,2,4 都差不多,但是第 3 步是不一樣的,炒土豆絲得拿鏟子翻炒,但是炒麻婆豆腐得拿勺子輕推,否則豆腐會爛(疫情宅在家,學了不少菜)。

使用場景

不同場景的處理流程,部分邏輯是通用的,可以放到父類中作為通用實現,部分邏輯是個性化的,需要子類去個性實現。

實踐經驗

還是接著之前語音播報的例子來說,后期我們新加了兩個需求:

  • 消息推送需要增加 trace。
  • 有些通道推送失敗需要重試。

所以現在的流程變成了這樣:

  • trace 開始。
  • 通道開始推送。
  • 是否允許重試,如果允許執行重試邏輯。
  • trace 結束。

其中 1 和 4 是通用的,2 和 3 是個性化的,鑒于此我在具體的推送策略之前增加了一層父類的策略,將通用邏輯放到了父類中。

修改后的代碼如下:

  1. abstract class AbstractPushStrategy implements PushStrategy{ 
  2.     @Override 
  3.     public CallResult push(AppDeviceVO deviceVO, Object content) { 
  4.         //1.構造span 
  5.         Span span = buildSpan(); 
  6.         //2.具體通道推送邏輯由子類實現 
  7.         CallResult callResult = doPush(deviceVO, content); 
  8.  
  9.         //3.是否允許重試邏輯由子類實現,如果允許執行重試邏輯 
  10.         if(!callResult.isSuccess() && canRetry()){ 
  11.             doPush(deviceVO, content); 
  12.         } 
  13.  
  14.         //4.trace結束 
  15.         span.finish()  
  16.     } 
  17.  
  18.     //具體推送邏輯由子類實現 
  19.     protected abstract CallResult doPush(AppDeviceVO deviceDO, Object content) ; 
  20.  
  21.     //是否允許重試由子類實現,有些通道之前沒有做消息排重,所有不能重試 
  22.     protected abstract boolean canRetry(CallResult callResult); 
  23.  
  24.  
  25. XingePushStrategy extends AbstractPushStrategy{ 
  26.     @Override 
  27.     protected CallResult doPush(AppDeviceVO deviceDO, Object content) { 
  28.         //執行推送邏輯 
  29.     } 
  30.  
  31.     @Override 
  32.     protected boolean canRetry(CallResult callResult){ 
  33.         return false 
  34.     } 

總結:通過模板定義了流程,將通用邏輯放在父類實現,減少了重復代碼,個性化邏輯由子類自己實現,子類間修改代碼互不干擾也不會破壞流程。

觀察者設計模式

模式定義

顧名思義,此模式需要有觀察者(Observer)和被觀察者(Observable)兩類角色。

當 Observable 狀態變化時會通知 Observer,Observer 一般會實現一類通用的接口。

比如 java.util.Observer,Observable 需要通知 Observer 時,逐個調用 Observer 的 update 方法即可,Observer 的處理成功與否不應該影響 Observable 的流程。

使用場景

一個對象(Observable)狀態改變需要通知其他對象,Observer 的存在不影響 Observable 的處理結果,Observer 的增刪對 Observable 無感知。

比如 Kafka 的消息訂閱,Producer 發送一條消息到 Topic,至于是 1 個還是 10 個 Consumer 訂閱這個 Topic,Producer 是不需要關注的。

實踐經驗

在責任鏈設計模式那塊我通過三個 Filter 解決了消費限制檢驗的問題,其中有一個 Filter 是用來檢驗消費次數的,我這里只是讀取用戶的消費次數,那么消費次數的累加是怎么完成的呢?

其實累加這塊就用到了觀察者模式,具體來講是這樣,當交易系統收到支付成功回調時會通過 Spring 的事件機制發布“支付成功事件”。

這樣負責累加消費次數和負責語音播報的訂閱者就會收到“支付成功事件”,進而做各自的業務邏輯。

畫個簡單的圖描述一下:

代碼結構大體如下:

  1. /** 
  2. 支付回調處理者 
  3. */ 
  4. PayCallBackController implements ApplicationContextAware { 
  5.      private ApplicationContext applicationContext; 
  6.  
  7.     //如果想獲取applicationContext需要實現ApplicationContextAware接口,Spring容器會回調setApplicationContext方法將applicationContext注入進來 
  8.     @Override 
  9.     public void setApplicationContext(ApplicationContext applicationContext) 
  10.             throws BeansException { 
  11.         this.applicationContext = applicationContext; 
  12.     } 
  13.      @RequestMapping(value = "/pay/callback.do"
  14.      public View callback(HttpServletRequest request){ 
  15.         if(paySuccess(request){ 
  16.             //構造支付成功事件 
  17.             PaySuccessEvent event = buildPaySuccessEvent(...); 
  18.             //通過applicationContext發布事件,從而達到通知觀察者的目的 
  19.             this.applicationContext.publishEvent(event); 
  20.         }  
  21.     } 
  22. /** 
  23.  * 語音播報處理者 
  24.  * 
  25.  */ 
  26. public class VoiceBroadcastHandler implements ApplicationListener<PaySuccessEvent>{ 
  27.     @Override 
  28.     public void onApplicationEvent(PaySuccessEvent event) { 
  29.         //語音播報邏輯 
  30.     } 
  31.  
  32. //其他處理者的邏輯類似 

總結:觀察者模式將被觀察者和觀察者之間做了解耦,觀察者存在與否不會影響被觀察者的現有邏輯。

裝飾器設計模式

模式定義

裝飾器用來包裝原有的類,在對使用者透明的情況下做功能的增強,比如 Java 中的 BufferedInputStream 可以對其包裝的 InputStream 做增強,從而提供緩沖功能。

使用場景

希望對原有類的功能做增強,但又不希望增加過多子類時,可以使用裝飾器模式來達到同樣的效果。

實踐經驗

筆者之前在推動整個公司接入 trace 體系,因此也提供了一些工具來解決 trace 的自動織入和上下文的自動傳遞。

為了支持線程間的上下文傳遞,我增加了 TraceRunnableWrapper 這個裝飾類,從而起到將父線程的上下文透傳到子線程中,對使用者完全透明。

代碼如下:

  1. /** 
  2. 可以自動攜帶trace上下文的Runnable裝飾器 
  3. */ 
  4. public class TraceRunnableWrapper implements Runnable{ 
  5.     //被包裝的目標對象 
  6.     private Runnable task; 
  7.     private Span parentSpan = null
  8.  
  9.     public TraceRunnableWrapper(Runnable task) { 
  10.         //1.獲取當前線程的上下文(因為new的時候還沒有發生線程切換,所以需要在這里將上下文獲取) 
  11.         //對這塊代碼感興趣的可以查看opentracing API 
  12.         io.opentracing.Scope currentScope = GlobalTracer.get().scopeManager().active(); 
  13.         //2.保存父上下文 
  14.         parentSpan = currentScope.span(); 
  15.         this.task = task; 
  16.     } 
  17.  
  18.     @Override 
  19.     public void run() { 
  20.         //run的時候將父線程的上下文綁定到當前線程 
  21.         io.opentracing.Scope scope = GlobalTracer.get().scopeManager().activate(parentSpan,false); 
  22.         task.run(); 
  23.     } 
  24.  
  25. //使用者 
  26. new Thread(new Runnable(){run(...)}).start()替換為new TraceRunnableWrapper(new Runnable(){run(...)}).start() 

總結:使用裝飾器模式做了功能的增強,對使用者來說只需要做簡單的組合就能繼續使用原功能。

外觀設計模式

模式定義

何為外觀,就是對外提供一個統一的入口:

  • 一是可以影藏系統內部的細節。
  • 二是可以降低使用者的復雜度。

比如 SpringMVC 中的 DispaterServlet,所有的 Controller 都是通過 DispaterServlet 統一暴露。

使用場景

降低使用者的復雜度,簡化客戶端的接入成本。

實踐經驗

筆者所在的公司對外提供了一些開放能力給第三方 ISV,比如設備管控、統一支付、對賬單下載等能力。

由于分屬于不同的團隊,所以對外提供的接口形式各異,初期還好,接口不多,ISV 也能接受,但是后期接口多了 ISV 就開始抱怨接入成本太高。

為了解決這一問題,我們在開放接口前面加了一層前端控制器 GatewayController,其實就是我們后來開放平臺的雛形。

GatewayController 對外統一暴露一個接口 gateway.do,將對外接口的請求參數和響應參數統一在 GatewayController 做收斂,GatewayController 往后端服務路由時也采用統一接口。

改造前后對比如下圖:

大概代碼如下:

  1. 使用者: 
  2. HttpClient.doPost("/gateway.do","{'method':'trade.create','sign':'wxxaaa','timestamp':'15311111111'},'bizContent':'業務參數'"
  3.  
  4. GatewayController: 
  5. @RequestMapping("/gateway.do"
  6. JSON gateway(HttpServletRequest req){ 
  7.    //1.組裝開放請求 
  8.    OpenRequest openRequest = buildOpenRequest(req); 
  9.  
  10.    OpenResponse openResponse = null
  11.    //2.請求路由 
  12.    if("trade.create".equals(openRequest.getMethod()){ 
  13.        //proxy to trade service by dubbo 
  14.        openResponse = TradeFacade.execute(genericParam); 
  15.    } else if("iot.message.push".equals(openRequest.getMethod()){ 
  16.        //proxy to iot service by httpclient 
  17.         openResponse = HttpClient.doPost('http://iot.service/generic/execute'genericParam); 
  18.    } 
  19.  
  20.    if(openResponse.isSuccess()){ 
  21.         return {"code":"10000","bizContent":openResponse.getResult()}; 
  22.    }else
  23.         return {"code":"20000","bizCode":openResponse.getCode()}; 
  24.    } 
  25.  
  26.  

總結:采用外觀模式屏蔽了系統內部的一些細節,降低了使用者的接入成本。

就拿 GatewayController 來說,ISV 的鑒權,接口的驗簽等重復工作統一由它實現。

ISV 對接不同的接口只需要關心一套接口協議接口,由 GatewayController 這一層做了收斂。

作者:踩刀詩人

編輯:陶家龍

出處:https://urlify.cn/J3mAna

 

責任編輯:武曉燕 來源: urlify.cn
相關推薦

2020-08-20 08:39:54

CTO代碼數據

2017-09-08 12:15:54

Python代碼Pythonic

2021-07-20 06:37:33

CTO代碼程序員

2010-02-02 10:08:19

CTO

2020-07-21 08:06:05

日志

2021-09-09 18:12:22

內存分段式網絡

2022-03-23 08:01:04

Python語言代碼

2024-08-12 00:00:00

NPMCTOJavaScrip

2018-09-30 09:36:58

CTO代碼程序員

2021-10-18 08:15:12

CTO代碼裁員

2017-03-21 15:01:47

BAT算法數據

2020-12-07 08:43:55

代碼軟件開發

2013-05-02 09:36:44

代碼項目

2024-10-29 09:25:00

2021-03-01 08:57:41

CTO代碼架構師

2011-09-08 14:24:57

51cto 51CTO

2020-03-20 08:00:32

代碼程序員追求

2018-02-25 11:00:34

代碼開發程序員

2020-01-07 14:44:09

GitHub代碼開發者

2012-07-11 11:05:16

編程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲风情在线观看 | 久久国产精品免费一区二区三区 | 91精品国产综合久久久久久丝袜 | 色婷婷久久久久swag精品 | 久草资源网站 | 国产精品大片 | 91就要激情| 91精品国产综合久久婷婷香蕉 | 久久新视频 | 黑人性hd | 久草日韩 | 福利社午夜影院 | 中文字幕 亚洲一区 | 日本超碰| 欧美一区二区另类 | 色综合色综合网色综合 | 99久久日韩精品免费热麻豆美女 | 黄色毛片免费 | 日本一区二区高清视频 | 米奇狠狠鲁 | 伦理一区二区 | 亚洲精品视频网站在线观看 | 黄色片网此| 六月婷婷久久 | 天天综合国产 | 99综合 | 青青久草 | 人人草人人干 | 人人玩人人干 | 日本特黄a级高清免费大片 国产精品久久性 | 一级毛片视频在线 | 亚洲h视频| 福利视频网址 | 国产精品久久久久一区二区三区 | 欧美精品1区2区 | www一级片 | 黑人巨大精品欧美黑白配亚洲 | 日韩毛片中文字幕 | 日韩视频区 | 欧美在线视频一区二区 | 免费激情av |