日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
扛不住了,老大讓我干掉if-else

圖片來(lái)自 Pexels

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),梅江企業(yè)網(wǎng)站建設(shè),梅江品牌網(wǎng)站建設(shè),網(wǎng)站定制,梅江網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,梅江網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

如何優(yōu)化這些 if-else 呢?本文分享一種設(shè)計(jì)模式:責(zé)任樹(shù)模式。

通過(guò)將責(zé)任鏈與策略模式融合,成為一種廣義的責(zé)任鏈模式,不僅可以完成任務(wù)的逐級(jí)委托,也可以在任一級(jí)選擇不同的下游策略進(jìn)行處理,并將責(zé)任樹(shù)模式抽象出一個(gè)通用的框架。

捫心自問(wèn),你在寫(xiě)業(yè)務(wù)代碼時(shí)是不是也習(xí)慣狂堆 if-else 呢?

問(wèn)題背景

最近開(kāi)發(fā)了一個(gè)需求,該接口需要根據(jù) p1、p2、p3、version 多個(gè)入?yún)⒌牟煌M合按照其對(duì)應(yīng)的業(yè)務(wù)策略給出結(jié)果數(shù)據(jù)。

由于該接口已經(jīng)開(kāi)發(fā)了三期了,每次開(kāi)發(fā)新一期的需求時(shí)為了兼容老的業(yè)務(wù)邏輯,大家都傾向于不刪不改只新增。

因此這塊代碼已經(jīng)產(chǎn)生了一些“壞味道”,函數(shù)入口通過(guò)不斷添加“衛(wèi)語(yǔ)句”判斷 version 的方式跳轉(zhuǎn)到新一期的業(yè)務(wù)邏輯方法中。

而每一期的業(yè)務(wù)邏輯也是通過(guò) p1、p2、p3 的 if-else 組合形成不同的分支邏輯。

這已經(jīng)是我簡(jiǎn)化后的表述,總之剛開(kāi)始對(duì)于我這個(gè)新同學(xué)來(lái)說(shuō),梳理這塊業(yè)務(wù)代碼著實(shí)花了一些功夫。

而且,這塊邏輯相當(dāng)于是一個(gè)業(yè)務(wù)上的通用能力,未來(lái)一定還會(huì)有五期、六期、N 期的需求進(jìn)來(lái),入?yún)⒌娜≈狄矔?huì)不斷拓展,因此以現(xiàn)有方式膨脹下去只會(huì)“壞味道”會(huì)越來(lái)越重。

總結(jié)一下,當(dāng)前場(chǎng)景面臨的問(wèn)題是:

  • 如何解決接口升級(jí),在保證兼容老版本的情況下輕松開(kāi)發(fā)新版本業(yè)務(wù)邏輯?
  • 如何根據(jù)入?yún)?p1、p2、p3 等的不同組合進(jìn)行策略定位?

解決思路

在思考解決方案時(shí),很容易想到兩種可以優(yōu)化類似場(chǎng)景的設(shè)計(jì)模式:責(zé)任鏈模式和策略模式。

責(zé)任鏈模式

責(zé)任鏈模式是實(shí)現(xiàn)了類似“流水線”結(jié)構(gòu)的逐級(jí)處理,通常是一條鏈?zhǔn)浇Y(jié)構(gòu),將“抽象處理者”的不同實(shí)現(xiàn)串聯(lián)起來(lái)。

如果當(dāng)前節(jié)點(diǎn)能夠處理任務(wù)則直接處理掉,如果無(wú)法處理則委托給責(zé)任鏈的下一個(gè)節(jié)點(diǎn),如此往復(fù)直到有節(jié)點(diǎn)可以處理這個(gè)任務(wù)。

我們可以通過(guò)責(zé)任鏈模式完成對(duì)不同 version 業(yè)務(wù)邏輯隔離的處理,比如節(jié)點(diǎn) 1 處理 version=1 的請(qǐng)求,節(jié)點(diǎn) 2 處理 version=2 的請(qǐng)求等等。

但問(wèn)題在于我們遇到的場(chǎng)景還需要根據(jù)一定策略,路由到不同的下游節(jié)點(diǎn)進(jìn)行處理。這就是策略模式擅長(zhǎng)解決的問(wèn)題了。

策略模式

策略模式的目的是將算法的使用與定義解耦,能夠?qū)崿F(xiàn)根據(jù)規(guī)則路由到不同策略類進(jìn)行處理。

我們可以通過(guò)策略模式解決根據(jù)不同參數(shù)組合執(zhí)行不同業(yè)務(wù)邏輯的場(chǎng)景。但是我們的場(chǎng)景僅僅通過(guò)一層策略路由無(wú)法滿足任務(wù)處理需求。請(qǐng)求的分層處理又是責(zé)任鏈模式所擅長(zhǎng)的了。

可以看到,兩種設(shè)計(jì)模式都不完全符合目前這個(gè)場(chǎng)景:責(zé)任鏈模式可以實(shí)現(xiàn)逐級(jí)委托,但每一級(jí)又不能像策略模式那樣路由到不同的處理者上;策略模式通常只有一層路由,不易實(shí)現(xiàn)多個(gè)參數(shù)的策略組合。

因此我們自然而然地可以想到:是不是可以將兩種模式結(jié)合起來(lái)?

廣義責(zé)任鏈模式:責(zé)任樹(shù)模式

將責(zé)任鏈與策略模式融合,即成為了一種廣義的責(zé)任鏈模式,我簡(jiǎn)稱為“責(zé)任樹(shù)模式”。

這種模式不僅可以完成任務(wù)的逐級(jí)委托,也可以在任一級(jí)選擇不同的下游策略進(jìn)行處理。

那么問(wèn)題來(lái)了,如何通過(guò)責(zé)任樹(shù)模式解決前面我們遇到的問(wèn)題呢?

首先看如何解決第一個(gè)問(wèn)題,新老接口的隔離和兼容:可以將接口每個(gè)版本的邏輯作為一個(gè)責(zé)任樹(shù)上第一層的不同實(shí)現(xiàn),如分別對(duì)應(yīng)上圖中的 Strategy1、Strategy2、Strategy3 節(jié)點(diǎn)。

這樣在接口入口,就首先把策略路由到不同的分支上去。如果沒(méi)有節(jié)點(diǎn)命中,則不再向下游委托直接返回錯(cuò)誤。

然后第二個(gè)問(wèn)題,參數(shù)的組合定位到不同的策略實(shí)現(xiàn)上:同樣的思路,一個(gè)參數(shù)對(duì)應(yīng)責(zé)任樹(shù)上的一層的路由,將該參數(shù)的不同取值路由到下一層的不同實(shí)現(xiàn)即可,這樣逐級(jí)委托,后面新增入?yún)⒌拿杜e值、甚至再拓展新的入?yún)⒍伎梢苑浅7奖愕剡M(jìn)行拓展。

優(yōu)化收益

將這塊業(yè)務(wù)通過(guò)“責(zé)任樹(shù)模式”重構(gòu)之后,可以收獲以下幾個(gè)收益點(diǎn):

  • 后續(xù)迭代人力成本降低。
  • 代碼結(jié)構(gòu)更清晰,可維護(hù)性提升:沒(méi)有了各種“衛(wèi)語(yǔ)句”的跳轉(zhuǎn) & 維護(hù)性巨差的巨型方法,函數(shù)可以收斂在理想的 50 行內(nèi)。
  • 后續(xù)新增需求修改代碼不易出錯(cuò):策略間隔離,不需要完整看一遍大函數(shù)理清邏輯再修改,只需要無(wú)腦添加一條路由 + 新的策略實(shí)現(xiàn)方法即可。
  • 問(wèn)題易定位:同樣由于策略間隔離,調(diào)試時(shí)可以直接定位到指定策略的業(yè)務(wù)邏輯代碼,不需要逐句排查。

相信有開(kāi)發(fā)經(jīng)驗(yàn)的同學(xué)應(yīng)該都有體會(huì),即使是自己寫(xiě)過(guò)的代碼,一陣子不看也會(huì)忘掉,等到再有修改時(shí),還要順著代碼理一遍邏輯,如果文檔、注釋沒(méi)寫(xiě)好,那就更加酸爽了。因此,將巨型函數(shù)拆分解耦非常重要。

抽象框架

雖然通過(guò)“責(zé)任樹(shù)模式”解決了我這個(gè)需求開(kāi)發(fā)中遇到的問(wèn)題,但是類似的問(wèn)題還是普遍存在的。

本著助(shǎo)人(zào)為(lún)樂(lè)(zi)的精神,我更進(jìn)一步,將責(zé)任樹(shù)模式抽象出一個(gè)通用的框架,方便大家在遇到類似問(wèn)題時(shí)快速“種樹(shù)”。

這個(gè)框架由一個(gè) Router 和 Handler 組成:

  • Router 是一個(gè)抽象類,負(fù)責(zé)定義如何路由到下游的多個(gè)子節(jié)點(diǎn)。
  • Handler 是接口,負(fù)責(zé)實(shí)現(xiàn)每個(gè)節(jié)點(diǎn)的業(yè)務(wù)邏輯。

我們可以非常方便地通過(guò) Router 和 Handler 的組合拼裝成整棵樹(shù)的結(jié)構(gòu)。

從圖中我們可以看出以下幾個(gè)要點(diǎn):

  • 除了根節(jié)點(diǎn)(入口)外,每個(gè)節(jié)點(diǎn)都實(shí)現(xiàn)了 Handler 接口。根節(jié)點(diǎn)只繼承 Router 抽象類。
  • 所有葉子節(jié)點(diǎn)只實(shí)現(xiàn) Handler 接口而無(wú)需繼承 Router 抽象類(無(wú)需再向下委托)。
  • 除了根節(jié)點(diǎn)和葉子節(jié)點(diǎn)外的其他節(jié)點(diǎn),都是上一層的 Handler,同時(shí)是下一層的 Router。

那么我們?cè)挷欢嗾f(shuō),先看下框架代碼。

①AbstractStrategyRouter 抽象類:

 
 
 
 
  1. /** 
  2.  * 通用的“策略樹(shù)“框架,通過(guò)樹(shù)形結(jié)構(gòu)實(shí)現(xiàn)分發(fā)與委托,每層通過(guò)指定的參數(shù)進(jìn)行向下分發(fā)委托,直到達(dá)到最終的執(zhí)行者。 
  3.  * 該框架包含兩個(gè)類:{@code StrategyHandler} 和 {@code AbstractStrategyRouter} 
  4.  * 其中:通過(guò)實(shí)現(xiàn) {@code AbstractStrategyRouter} 抽象類完成對(duì)策略的分發(fā), 
  5.  * 實(shí)現(xiàn) {@code StrategyHandler} 接口來(lái)對(duì)策略進(jìn)行實(shí)現(xiàn)。 
  6.  * 像是第二層 A、B 這樣的節(jié)點(diǎn),既是 Root 節(jié)點(diǎn)的策略實(shí)現(xiàn)者也是策略A1、A2、B1、B2 的分發(fā)者,這樣的節(jié)點(diǎn)只需要 
  7.  * 同時(shí)繼承 {@code StrategyHandler} 和實(shí)現(xiàn) {@code AbstractStrategyRouter} 接口就可以了。 
  8.  * 
  9.  * 
     
  10.  *           +---------+ 
  11.  *           |  Root   |   ----------- 第 1 層策略入口 
  12.  *           +---------+ 
  13.  *            /       \  ------------- 根據(jù)入?yún)?nbsp;P1 進(jìn)行策略分發(fā) 
  14.  *           /         \ 
  15.  *     +------+      +------+ 
  16.  *     |  A   |      |  B   |  ------- 第 2 層不同策略的實(shí)現(xiàn) 
  17.  *     +------+      +------+ 
  18.  *       /  \          /  \  --------- 根據(jù)入?yún)?nbsp;P2 進(jìn)行策略分發(fā) 
  19.  *      /    \        /    \ 
  20.  *   +---+  +---+  +---+  +---+ 
  21.  *   |A1 |  |A2 |  |B1 |  |B2 |  ----- 第 3 層不同策略的實(shí)現(xiàn) 
  22.  *   +---+  +---+  +---+  +---+ 
  23.  * 
 
  •  * 
  •  * @author 
  •  * @date 
  •  * @see StrategyHandler 
  •  */ 
  • @Component 
  • public abstract class AbstractStrategyRouter { 
  •  
  •     /** 
  •      * 策略映射器,根據(jù)指定的入?yún)⒙酚傻綄?duì)應(yīng)的策略處理者。 
  •      * 
  •      * @param  策略的入?yún)㈩愋?nbsp;
  •      * @param  策略的返回值類型 
  •      */ 
  •     public interface StrategyMapper { 
  •         /** 
  •          * 根據(jù)入?yún)@取到對(duì)應(yīng)的策略處理者??赏ㄟ^(guò) if-else 實(shí)現(xiàn),也可通過(guò) Map 實(shí)現(xiàn)。 
  •          * 
  •          * @param param 入?yún)?nbsp;
  •          * @return 策略處理者 
  •          */ 
  •         StrategyHandler get(T param); 
  •     } 
  •  
  •     private StrategyMapper strategyMapper; 
  •  
  •     /** 
  •      * 類初始化時(shí)注冊(cè)分發(fā)策略 Mapper 
  •      */ 
  •     @PostConstruct 
  •     private void abstractInit() { 
  •         strategyMapper = registerStrategyMapper(); 
  •         Objects.requireNonNull(strategyMapper, "strategyMapper cannot be null"); 
  •     } 
  •  
  •     @Getter 
  •     @Setter 
  •     @SuppressWarnings("unchecked") 
  •     private StrategyHandler defaultStrategyHandler = StrategyHandler.DEFAULT; 
  •  
  •     /** 
  •      * 執(zhí)行策略,框架會(huì)自動(dòng)根據(jù)策略分發(fā)至下游的 Handler 進(jìn)行處理 
  •      * 
  •      * @param param 入?yún)?nbsp;
  •      * @return 下游執(zhí)行者給出的返回值 
  •      */ 
  •     public R applyStrategy(T param) { 
  •         final StrategyHandler strategyHandler = strategyMapper.get(param); 
  •         if (strategyHandler != null) { 
  •             return strategyHandler.apply(param); 
  •         } 
  •  
  •         return defaultStrategyHandler.apply(param); 
  •     } 
  •  
  •     /** 
  •      * 抽象方法,需要子類實(shí)現(xiàn)策略的分發(fā)邏輯 
  •      * 
  •      * @return 分發(fā)邏輯 Mapper 對(duì)象 
  •      */ 
  •     protected abstract StrategyMapper registerStrategyMapper(); 
  • 繼承 AbstractStrategyRouter

    如果子節(jié)點(diǎn)路由邏輯比較簡(jiǎn)單,可以直接通過(guò) if-else 進(jìn)行分發(fā)。當(dāng)然如果為了更好地性能、適應(yīng)更復(fù)雜的分發(fā)邏輯也可以使用 Map 等保存映射。

    對(duì)于實(shí)現(xiàn)了該抽象類的 Router 節(jié)點(diǎn),只需要調(diào)用其 public R applyStrategy(T param) 方法即可獲取該節(jié)點(diǎn)的期望輸出。

    框架會(huì)自動(dòng)根據(jù)定義的路由邏輯將 param 傳遞到對(duì)應(yīng)的子節(jié)點(diǎn),再由子節(jié)點(diǎn)不斷向下分發(fā)直到葉子節(jié)點(diǎn)或可以給出業(yè)務(wù)輸出的一層。這個(gè)過(guò)程有點(diǎn)類似遞歸或者分治的思想。

    ②StrategyHandler 接口:

     
     
     
     
    1. /** 
    2.  * @author 
    3.  * @date 
    4.  */ 
    5. public interface StrategyHandler { 
    6.  
    7.     @SuppressWarnings("rawtypes") 
    8.     StrategyHandler DEFAULT = t -> null; 
    9.  
    10.     /** 
    11.      * apply strategy 
    12.      * 
    13.      * @param param 
    14.      * @return 
    15.      */ 
    16.     R apply(T param); 

    除了根節(jié)點(diǎn)外,都要實(shí)現(xiàn) StrategyHandler

    因此不再需要同時(shí)繼承 AbstractStrategyRouter

    對(duì)于其他責(zé)任樹(shù)中的中間層節(jié)點(diǎn),都需要同時(shí)繼承 Router 抽象類和實(shí)現(xiàn) Handler 接口。

    在 R apply(T param); 方法中首先進(jìn)行一定異常入?yún)r截,遵循 fail-fast 原則,避免將這一層可以攔截的錯(cuò)誤傳遞到下一層,同時(shí)也要避免“越權(quán)”做非本層職責(zé)的攔截校驗(yàn),避免產(chǎn)生耦合,為后面業(yè)務(wù)拓展挖坑。

    在攔截邏輯后直接調(diào)用本身 Router 的 public R applyStrategy(T param) 方法路由給下游節(jié)點(diǎn)即可。

    完結(jié)撒花

    至此,關(guān)于如何通過(guò)“責(zé)任樹(shù)模式”優(yōu)化這個(gè)需求場(chǎng)景的介紹就基本結(jié)束了,這不是一個(gè)復(fù)雜的需求,更不是一個(gè)多么精妙的優(yōu)化,這只是日常需求開(kāi)發(fā)中通過(guò)設(shè)計(jì)模式優(yōu)化代碼的一個(gè)小例子。

    最后再簡(jiǎn)單聊聊我在日常需求開(kāi)發(fā)過(guò)程中關(guān)于架構(gòu)設(shè)計(jì)部分的一些思考。

    其實(shí)并不是說(shuō)用“if-else”很 Low,用設(shè)計(jì)模式就 Niubility,二者各有其擅長(zhǎng)的應(yīng)用場(chǎng)景,在合適的場(chǎng)景使用合適的代碼才是正道。

    其實(shí)“if-else”足以滿足大部分日常需求的開(kāi)發(fā),且簡(jiǎn)單、靈活、可靠。

    這里的“if-else”泛指樸素直白的編程模式,僅以實(shí)現(xiàn)需求業(yè)務(wù)功能為目的的編碼方式。

    當(dāng)然,有些同學(xué)不滿足于此,希望可以通過(guò)經(jīng)過(guò)思考的、更優(yōu)的架構(gòu)設(shè)計(jì)使代碼變的更簡(jiǎn)潔、拓展性更好、性能更優(yōu)、可讀性更好等等。

    不過(guò)對(duì)于此也存在反對(duì)的論述,謂之“過(guò)早優(yōu)化乃萬(wàn)惡之源”。

    這句話源自 Donald Knuth 他老人家:

    we should forget about small efficiencies,say about 97% of the time:premature optimization is the root of all evil.

    這句話我當(dāng)然承認(rèn)其正確性,但我同樣覺(jué)得需要注意以下幾點(diǎn):

    ①任何“結(jié)論”都有其所處背景、上下文細(xì)節(jié)等,通過(guò)一句話指導(dǎo)工作是不成立的。

    優(yōu)秀的架構(gòu)師可以給出架構(gòu)設(shè)計(jì)是在理論基礎(chǔ)、大量實(shí)踐、不斷思考總結(jié)以及無(wú)數(shù)采坑的經(jīng)驗(yàn)的基礎(chǔ)上得來(lái)的,而不是他知道一句別人都不知道的“咒語(yǔ)”。

    ②Knuth 這句話更偏重于反對(duì)奇技淫巧、細(xì)枝末節(jié)的性能優(yōu)化,因?yàn)樵凇斑^(guò)早”的時(shí)候無(wú)法準(zhǔn)確獲知系統(tǒng)的瓶頸且局部的優(yōu)化不僅不能帶來(lái)收益,反而會(huì)造成更大的代價(jià)。

    他批評(píng)的恰恰是不著眼于整體架構(gòu)的局部視角對(duì)系統(tǒng)的破壞,而架構(gòu)設(shè)計(jì)正是需要從整體視角去做選擇與權(quán)衡。因此將 Knuth 這句話直接推廣到“架構(gòu)設(shè)計(jì)”上并不妥當(dāng)。

    ③很多人覺(jué)得在項(xiàng)目開(kāi)發(fā)時(shí)需求經(jīng)?!八蚕⑷f(wàn)變”、“朝令夕改”,而做優(yōu)化又需要花費(fèi)大量時(shí)間思考,根本沒(méi)有精力優(yōu)化。

    我認(rèn)為這種論述也是不成立的,憑什么認(rèn)為等到壞味道嚴(yán)重、歷史包袱沉重的時(shí)候就有精力、能力和膽量做優(yōu)化了呢?

    ④何時(shí)是所謂的“不早”很難界定,其實(shí)我們永遠(yuǎn)都無(wú)法確定自己掌握了足夠的細(xì)節(jié)可以進(jìn)行絕對(duì)正確的優(yōu)化。

    在現(xiàn)實(shí)世界中,受到時(shí)間維度的限制,我們永遠(yuǎn)無(wú)法達(dá)成全局最優(yōu),只能以局部最優(yōu)不斷去逼近全局最優(yōu)。我覺(jué)得等到壞味道嚴(yán)重不得不重構(gòu)的時(shí)候才想起優(yōu)化已為時(shí)過(guò)晚。

    ⑤這句話不應(yīng)該成為不做設(shè)計(jì)的借口,即使最終提交的代碼仍是“if-else”版本,也不應(yīng)省略思考、推演、權(quán)衡的過(guò)程,日常需求是練兵場(chǎng),是精進(jìn)技術(shù)的必經(jīng)之路。

    所以,我覺(jué)得不要被這句話束縛手腳,當(dāng)然更不要閉門(mén)造車,在開(kāi)發(fā)過(guò)程中勤于思考,向更有經(jīng)驗(yàn)的人請(qǐng)教,在架構(gòu)設(shè)計(jì)上不斷學(xué)習(xí)、探索,才能擺脫日復(fù)一日通過(guò)“if-else”堆砌業(yè)務(wù)邏輯的循環(huán)。

    作者:尋弈

    編輯:陶家龍

    出處:轉(zhuǎn)載自公眾號(hào)閑魚(yú)技術(shù)(ID:XYtech_Alibaba)


    當(dāng)前文章:扛不住了,老大讓我干掉if-else
    瀏覽地址:http://www.dlmjj.cn/article/cdoccip.html