女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

設計模式最佳實踐探索—策略模式

OSC開源社區 ? 來源:OSC開源社區 ? 作者:OSC開源社區 ? 2022-10-31 14:24 ? 次閱讀

根據不同的應用場景與意圖,設計模式主要分為創建型模式、結構型模式和行為型模式三類。本文主要探索行為型模式中的策略模式如何更好地應用于實踐中。

前言

在軟件開發的過程中,需求的多變性幾乎是不可避免的,而作為一名服務端開發人員,我們所設計的程序應盡可能支持從技術側能夠快速、穩健且低成本地響應紛繁多變的業務需求,從而推進業務小步快跑、快速迭代。設計模式正是前輩們針對不同場景下不同類型的問題,所沉淀下來的一套程序設計思想與解決方案,用來提高代碼可復用性、可維護性、可讀性、穩健性以及安全性等。下面是設計模式的祖師爺GoF(Gang of Four,四人幫)的合影,感受一下大佬的氣質~

靈活應用設計模式不僅可以使程序本身具有更好的健壯性、易修改性和可擴展性,同時它使得編程變得工程化,對于多人協作的大型項目,能夠降低維護成本、提升多人協作效率。根據不同的應用場景與意圖,設計模式主要分為三類,分別為創建型模式、結構型模式和行為型模式。本文主要探索行為型模式中的策略模式如何更好地應用于實踐中。

使用場景

策略模式屬于對象的行為模式,其用意是針對一組可替換的算法,將每一個算法封裝到具有共同接口的獨立的類中,使得算法可以在不影響到客戶端(算法的調用方)的情況下發生變化,使用策略模式可以將算法的定義與使用隔離開來,保證類的單一職責原則,使得程序整體符合開閉原則。

以手淘中商詳頁的店鋪卡片為例,店鋪卡片主要包含店鋪名稱、店鋪logo、店鋪類型以及店鋪等級等信息,其中不同店鋪類型的店鋪等級計算邏輯是不同的,為了獲取店鋪等級,可以采用如下所示代碼:

 if (Objects.equals("淘寶", shopType)) {
   // 淘寶店鋪等級計算邏輯
   // return 店鋪等級;
 } else if (Objects.equals("天貓", shopType)) {
   // 天貓店鋪等級計算邏輯
   // return 店鋪等級
 } else if (Objects.equals("淘特", shopType)) {
   // 淘特店鋪等級計算邏輯
   // return 店鋪等級
 } else {
   //  ...
 }
這種寫法雖然實現簡單,但使得各類店鋪等級計算邏輯與程序其他邏輯相耦合,未來如果要對其中一種計算邏輯進行更改或者新增加一種計算邏輯,將不得不對原有代碼進行更改,違背了OOP的單一職責原則與開閉原則,讓代碼的維護變得困難。若項目本身比較復雜,去改動項目原有的邏輯是一件非常耗時又風險巨大的事情。此時我們可以采取策略模式來處理,將不同類型的店鋪等級計算邏輯封裝到具有共同接口又互相獨立的類中,其核心類圖如下所示: ?

1c93fdcc-56d1-11ed-a3b6-dac502259ad0.png

這樣一來,程序便具有了良好的可擴展性與易修改性,若想增加一種新的店鋪等級計算邏輯,則可將其對應的等級計算邏輯單獨封裝成ShopRankHandler接口的實現類即可,同樣的,若想對其中一種策略的實現進行更改,在相應的實現類中進行更改即可,而不用侵入原有代碼中去開發。

最佳實踐探索

本節仍以店鋪等級的處理邏輯為例,探索策略模式的最佳實踐。當使用策略模式的時候,會將一系列算法用具有相同接口的策略類封裝起來,客戶端想調用某一具體算法,則可分為兩個步驟:1、某一具體策略類對象的獲取;2、調用策略類中封裝的算法。比如客戶端接受到的店鋪類型為“天貓”,則首先需要獲取TmShopRankHandleImpl類對象,然后調用其中的算法進行天貓店鋪等級的計算。在上述兩個步驟中,步驟2是依賴于步驟1的,當步驟1完成之后,步驟2也隨之完成,因此上述步驟1成為整個策略模式中的關鍵。

下面列舉幾種策略模式的實現方式,其區別主要在于具體策略類對象獲取的方式不同,對其優缺點進行分析,并探索其最佳實踐。

?暴力法

店鋪等級計算策略接口

public interface ShopRankHandler {
    /**
    * 計算店鋪等級
    * @return 店鋪等級
    */
    
    public String calculate();
}

各類型店鋪等級計算策略實現類

淘寶店

public class TbShopRankHandleImpl implements ShopRankHandler{
    @Override
    public String calculate() {
        // 具體計算邏輯
        return rank;
    }
}

天貓店

public class TmShopRankHandleImpl implements ShopRankHandler{
    @Override
    public String calculate() {
        // 具體計算邏輯
        return rank;
    }
}

淘特店

public class TtShopRankHandleImpl implements ShopRankHandler{
    @Override
    public String calculate() {
        // 具體計算邏輯
        return rank;
    }
}

客戶端調用

// 根據參數調用對應的算法計算店鋪等級
public String acqurireShopRank(String shopType) {
    String rank = StringUtil.EMPTY_STRING;
    if (Objects.equals("淘寶", shopType)) {
        // 獲取淘寶店鋪等級計算策略類
        ShopRankHandler shopRankHandler = new TbShopRankHandleImpl();
        // 計算店鋪等級
        rank = shopRankHandler.calculate();
    } else if (Objects.equals("天貓", shopType)) {
        // 獲取天貓店鋪等級計算策略類
        ShopRankHandler shopRankHandler = new TmShopRankHandleImpl();
        // 計算店鋪等級
        rank = shopRankHandler.calculate();
    } else if (Objects.equals("淘特", shopType)) {
        // 獲取淘特店鋪等級計算策略類
        ShopRankHandler shopRankHandler = new TtShopRankHandleImpl();
        // 計算店鋪等級
        rank = shopRankHandler.calculate();
    } else {
        //  ...
    }
    return rank;
}

效果

至此,當我們需要新增策略類時,需要做的改動如下:

新建策略類并實現策略接口

改動客戶端的if else分支

優點

將店鋪等級計算邏輯單獨進行封裝,使其與程序其他邏輯解耦,具有良好的擴展性。

實現簡單,易于理解。

缺點

客戶端與策略類仍存在耦合,當需要增加一種新類型店鋪時,除了需要增加新的店鋪等級計算策略類,客戶端需要改動if else分支,不符合開閉原則。

?第一次迭代(枚舉+簡單工廠)

有沒有什么方法能使客戶端與具體的策略實現類徹底進行解耦,使得客戶端對策略類的擴展實現“零”感知?在互聯網領域,沒有什么問題是加一層解決不了的,我們可以在客戶端與眾多的策略類之間加入工廠來進行隔離,使得客戶端只依賴工廠,而具體的策略類由工廠負責產生,使得客戶端與策略類解耦,具體實現如下所示:

枚舉類

public enum ShopTypeEnum {
    TAOBAO("A","淘寶"),
    TMALL("B", "天貓"),
    TAOTE("C", "淘特");
    
    @Getter
    private String type;
    @Getter
    private String desc;
    ShopTypeEnum(String type, String des) {
        this.type = type;
        this.desc = des;
    }
}

店鋪等級計算接口

public interface ShopRankHandler {
    /**
    * 計算店鋪等級
    * @return 店鋪等級
    */
    
    String calculate();
}

各類型店鋪等級計算策略實現類

淘寶店

public class TbShopRankHandleImpl implements ShopRankHandler{   
    @Override
    public String calculate() {
        // 具體計算邏輯
        return rank;
    }
}

天貓店

public class TmShopRankHandleImpl implements ShopRankHandler{
    @Override
    public String calculate() {
        // 具體計算邏輯
        return rank;
    }
}

淘特店

public class TtShopRankHandleImpl implements ShopRankHandler{
    @Override
    public String calculate() {
        // 具體計算邏輯
        return rank;
    }
}

策略工廠類

@Component
public class ShopRankHandlerFactory {
    
    // 初始化策略beans
    private static final Map GET_SHOP_RANK_STRATEGY_MAP = ImmutableMap.builder()
        .put(ShopTypeEnum.TAOBAO.getType(), new TbShopRankHandleImpl())
        .put(ShopTypeEnum.TMALL.getType(), new TmShopRankHandleImpl())
        .put(ShopTypeEnum.TAOTE.getType(), new TtShopRankHandleImpl())
        ;


    /**
     * 根據店鋪類型獲取對應的獲取店鋪卡片實現類
     *
     * @param shopType 店鋪類型
     * @return 店鋪類型對應的獲取店鋪卡片實現類
     */
    public ShopRankHandler getStrategy(String shopType) {
        return GET_SHOP_RANK_STRATEGY_MAP.get(shopType);
    }


}

客戶端調用

@Resource
ShopRankHandlerFactory shopRankHandlerFactory;
// 根據參數調用對應的算法計算店鋪等級
public String acqurireShopRank(String shopType) {
    ShopRankHandler shopRankHandler = shopRankHandlerFactory.getStrategy(shopType);
    return Optional.ofNullable(shopRankHandler)
        .map(shopRankHandle -> shopRankHandle.calculate())
        .orElse(StringUtil.EMPTY_STRING);
}

效果

至此,當我們需要新增策略類時,需要做的改動如下:

新建策略類并實現策略接口

增加枚舉類型

工廠類中初始化時增加新的策略類

相比上一種方式,策略類與客戶端進行解耦,無需更改客戶端的代碼。

優點

將客戶端與策略類進行解耦,客戶端只面向策略接口進行編程,對具體策略類的變化(更改、增刪)完全無感知,符合開閉原則。

缺點

需要引入額外的工廠類,使系統結構變得復雜。

當新加入策略類時,工廠類中初始化策略的部分仍然需要改動。

?第二次迭代(利用Spring框架初始化策略beans)

在枚舉+簡單工廠實現的方式中,利用簡單工廠將客戶端與具體的策略類實現進行了解耦,但工廠類中初始化策略beans的部分仍然與具體策略類存在耦合,為了進一步解耦,我們可以利用Spring框架中的InitializingBean接口與ApplicationContextAware接口來實現策略beans的自動裝配。InitializingBean接口中的afterPropertiesSet()方法在類的實例化過程當中執行,也就是說,當客戶端完成注入ShopRankHandlerFactory工廠類實例的時候,afterPropertiesSet()也已經執行完成。因此我們可以通過重寫afterPropertiesSet()方法,在其中利用getBeansOfType()方法來獲取到策略接口的所有實現類,并存于Map容器之中,達到工廠類與具體的策略類解耦的目的。相比于上一種實現方式,需要改動的代碼如下:

店鋪等級計算接口

public interface ShopRankHandler {
    /**
    * 獲取店鋪類型的方法,接口的實現類需要根據各自的枚舉類型來實現,后面就不貼出實現類的代碼
    * @return 店鋪等級
    */
    String getType();
    /**
    * 計算店鋪等級
    * @return 店鋪等級
    */
    String calculate();
}

策略工廠類

@Component
public class ShopRankHandlerFactory implements InitializingBean, ApplicationContextAware {


    private ApplicationContext applicationContext;
    /**
     * 策略實例容器
     */
    private Map GET_SHOP_RANK_STRATEGY_MAP;


    /**
     * 根據店鋪類型獲取對應的獲取店鋪卡片實現類
     *
     * @param shopType 店鋪類型
     * @return 店鋪類型對應的獲取店鋪卡片實現類
     */
    public ShopRankHandler getStrategy(String shopType) {
        return GET_SHOP_RANK_STRATEGY_MAP.get(shopType);
    }


    @Override
    public void afterPropertiesSet() {
        Map beansOfType = applicationContext.getBeansOfType(ShopRankHandler.class);


        GET_SHOP_RANK_STRATEGY_MAP = Optional.ofNullable(beansOfType)
                            .map(beansOfTypeMap -> beansOfTypeMap.values().stream()
                                    .filter(shopRankHandle -> StringUtils.isNotEmpty(shopRankHandle.getType()))
                                    .collect(Collectors.toMap(ShopRankHandler::getType, Function.identity())))
                            .orElse(new HashMap<>(8));
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


}

效果

至此,當我們需要新增策略類時,需要做的改動如下:

新建策略類并實現策略接口

增加枚舉類型

相比于上一種方式,可以省略工廠類在初始化策略beans時要增加新的策略類這一步驟。

優點

借助Spring框架完成策略beans的自動裝配,使得策略工廠類與具體的策略類進一步解耦。

缺點

需要借助Spring框架來完成,不過在Spring框架應用如此廣泛的今天,這個缺點可以忽略不計。

?最終迭代(利用泛型進一步提高策略工廠復用性)

經過上面兩次迭代以后,策略模式的實現已經變得非常方便,當需求發生改變的時候,我們再也不用手忙腳亂了,只需要關注新增或者變化的策略類就好,而不用侵入原有邏輯去開發。但是還有沒有改進的空間呢?

設想一下有一個新業務同樣需要策略模式來實現,如果為其重新寫一個策略工廠類,整個策略工廠類中除了新的策略接口外,其他代碼均與之前的策略工廠相同,出現了大量重復代碼,這是我們所不能忍受的。為了最大程度避免重復代碼的出現,我們可以使用泛型將策略工廠類中的策略接口參數化,使其變得更靈活,從而提高其的復用性。

理論存在,實踐開始!代碼示意如下:

定義泛型接口

public interface GenericInterface {
     E getType();
}

定義策略接口繼承泛型接口

public interface StrategyInterfaceA extends GenericInterface{


    String handle();
}
public interface StrategyInterfaceB extends GenericInterface{


    String handle();
}
public interface StrategyInterfaceC extends GenericInterface{


    String handle();
}

實現泛型策略工廠

public class HandlerFactory> implements InitializingBean, ApplicationContextAware {
    private ApplicationContext applicationContext;
    /**
     * 泛型策略接口類型
     */
    private Class strategyInterfaceType;


    /**
     * java泛型只存在于編譯期,無法通過例如T.class的方式在運行時獲取其類信息
     * 因此利用構造函數傳入具體的策略類型class對象為getBeansOfType()方法
     * 提供參數
     *
     * @param strategyInterfaceType 要傳入的策略接口類型
     */
    public HandlerFactory(Class strategyInterfaceType) {
        this.strategyInterfaceType = strategyInterfaceType;
    }
    /**
     * 策略實例容器
     */
    private Map GET_SHOP_RANK_STRATEGY_MAP;
    /**
     * 根據不同參數類型獲取對應的接口實現類
     *
     * @param type 參數類型
     * @return 參數類型對應的接口實現類
     */
    public T getStrategy(E type) {
        return GET_SHOP_RANK_STRATEGY_MAP.get(type);
    }


    @Override
    public void afterPropertiesSet() {
        Map beansOfType = applicationContext.getBeansOfType(strategyInterfaceType);
        System.out.println(beansOfType);


        GET_SHOP_RANK_STRATEGY_MAP = Optional.ofNullable(beansOfType)
                .map(beansOfTypeMap -> beansOfTypeMap.values().stream()
                        .filter(strategy -> StringUtils.isNotEmpty(strategy.getType().toString()))
                        .collect(Collectors.toMap(strategy -> strategy.getType(), Function.identity())))
                .orElse(new HashMap<>(8));
        System.out.println(GET_SHOP_RANK_STRATEGY_MAP);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

有了上述泛型策略工廠類,當我們需要新建一個策略工廠類的時候,只需要利用其構造函數傳入相應的策略接口即可。生成StrategyInterfaceA、StrategyInterfaceB與StrategyInterfaceC接口的策略工廠如下:

public class BeanConfig {
    @Bean
    public HandlerFactory strategyInterfaceAFactory(){
        return new HandlerFactory<>(StrategyInterfaceA.class);
    }
    @Bean
    public HandlerFactory strategyInterfaceBFactory(){
        return new HandlerFactory<>(StrategyInterfaceB.class);
    }
    @Bean
    public HandlerFactory strategyInterfaceCFactory(){
        return new HandlerFactory<>(StrategyInterfaceC.class);
    }
  
}

效果

此時,若想新建一個策略工廠,則只需將策略接口作為參數傳入泛型策略工廠即可,無需再寫重復的樣板代碼,策略工廠的復用性大大提高,也大大提高了我們的開發效率。

優點

將策略接口類型參數化,策略工廠不受接口類型限制,成為任意接口的策略工廠。

缺點

系統的抽象程度、復雜度變高,不利于直觀理解。

結束語

學習設計模式,關鍵是學習設計思想,不能簡單地生搬硬套,靈活正確地應用設計模式可以讓我們在開發中取得事半功倍的效果,但也不能為了使用設計模式而過度設計,要合理平衡設計的復雜度和靈活性。

本文是對策略模式最佳實踐的一次探索,不一定是事實上的最佳實踐,歡迎大家指正與討論。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8926

    瀏覽量

    153165
  • 設計模式
    +關注

    關注

    0

    文章

    53

    瀏覽量

    8765

原文標題:設計模式最佳實踐探索—策略模式

文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    “模電”基于ICT整合教學模式理論與實踐探索課題研究實施方案

    “模電”基于ICT整合教學模式理論與實踐探索課題研究實施方案湖南衡陽師范學院課題組:陳列尊、張登玉、游開明、譚岳衡劉艷波、陳衛東、陳揚、尹軍、譚曉蘭、陳瑾一、本課題研究的意義“模擬電子技術
    發表于 09-28 09:41

    Dockerfile的最佳實踐

    ”微服務一條龍“最佳指南-“最佳實踐”篇:Dockerfile
    發表于 07-11 16:22

    混合導通模式BoostPFC的控制策略研究

    混合導通模式BoostPFC的控制策略研究_王武
    發表于 01-04 16:32 ?8次下載

    數字信號處理課程分類和分層教學模式探索

    數字信號處理課程分類和分層教學模式探索
    發表于 02-07 14:58 ?5次下載

    OpenHarmony教育專場:OpenHarmony師資培訓模式探索

    關于OpenHarmony師資培訓模式探索和教學實訓產品及平臺介紹。
    的頭像 發表于 12-28 15:11 ?1231次閱讀
    OpenHarmony教育專場:OpenHarmony師資培訓<b class='flag-5'>模式</b><b class='flag-5'>探索</b>

    探索粒子光子的低功率模式的郵件DIY

    電子發燒友網站提供《探索粒子光子的低功率模式的郵件DIY.zip》資料免費下載
    發表于 10-18 09:33 ?0次下載
    <b class='flag-5'>探索</b>粒子光子的低功率<b class='flag-5'>模式</b>的郵件DIY

    探索寬泛 Vin DC/DC 轉換的電流模式控制

    探索寬泛 Vin DC/DC 轉換的電流模式控制
    發表于 11-04 09:52 ?0次下載
    <b class='flag-5'>探索</b>寬泛 Vin DC/DC 轉換的電流<b class='flag-5'>模式</b>控制

    為什么我不再推薦枚舉策略模式

    我們可以看到經典方法,創建了一個接口、三個策略類,還是比較啰嗦的。調用類的實現也待商榷,新增一個策略類還要修改榜單實例(可以用抽象工廠解決,但是復雜度又上升了)。加之我們有更好的選擇,所以此處不再推薦經典策略
    的頭像 發表于 04-14 10:52 ?2279次閱讀

    基于輸入阻抗控制的多模式混合PFC的控制策略

    簡單地說,混合PFC的控制策略就是操縱開關頻率在正弦電壓內進行變化來進行跨越多個區域,難點是多模式區域的增益不會統一,實現多模式優秀的電流控制效果就是難題
    的頭像 發表于 04-25 14:20 ?1537次閱讀
    基于輸入阻抗控制的多<b class='flag-5'>模式</b>混合PFC的控制<b class='flag-5'>策略</b>

    部署Linux的最佳實踐探索

    編者按:本文節選自節選自《基于Linux的企業自動化》第五章?!暗?章,使用Ansible構建用于部署的虛擬機模板,通過構建虛擬機模板來探索部署Linux的最佳實踐,虛擬機模板將以實際操作的方式大規模部署在虛擬機管理程序上?!?/div>
    的頭像 發表于 05-16 09:35 ?727次閱讀

    設計模式行為型:策略模式

    策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬于行為型模式。
    的頭像 發表于 06-07 11:18 ?821次閱讀
    設計<b class='flag-5'>模式</b>行為型:<b class='flag-5'>策略</b><b class='flag-5'>模式</b>

    什么是策略模式

    什么是策略模式 官話: 策略模式(Strategy Pattern): 定義一系列算法類,將每一個算法封裝起來,并讓它們可以相互替換,策略
    的頭像 發表于 10-08 14:15 ?3152次閱讀
    什么是<b class='flag-5'>策略</b><b class='flag-5'>模式</b>

    如何通過策略模式簡化if-else

    相信大家日常開發中會經常寫各種分支判斷語句,比如 if-else ,當分支較多時,代碼看著會比較臃腫,那么如何優化呢? 1、什么是策略模式? Define a family
    的頭像 發表于 10-08 16:08 ?925次閱讀
    如何通過<b class='flag-5'>策略</b><b class='flag-5'>模式</b>簡化if-else

    實踐GoF的23種設計模式:備忘錄模式

    相對于代理模式、工廠模式等設計模式,備忘錄模式(Memento)在我們日常開發中出鏡率并不高,除了應用場景的限制之外,另一個原因,可能是備忘錄模式
    的頭像 發表于 11-25 09:05 ?764次閱讀
    <b class='flag-5'>實踐</b>GoF的23種設計<b class='flag-5'>模式</b>:備忘錄<b class='flag-5'>模式</b>

    設計模式-策略模式

    作者:京東工業 孫磊 一、概念 策略模式(Strategy Pattern)也稱為(Policy Parttern)。 它定義了算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變換
    的頭像 發表于 01-08 13:47 ?289次閱讀
    設計<b class='flag-5'>模式</b>-<b class='flag-5'>策略</b><b class='flag-5'>模式</b>