重學 Java 設計模式:實戰組合模式(營銷差別化人羣發券,決策樹引擎搭建場景)

做者:小傅哥
博客:https://bugstack.cnhtml

沉澱、分享、成長,讓本身和他人都能有所收穫!😄

1、前言

小朋友才作選擇題,成年人我都要java

頭幾年只要羣裏一問我該學哪一個開發語言,哪一個語言最好。羣裏確定聊的特別火熱,有人支持PHP、有人喊號Java、也有C++和C#。但這幾年開始好像你們並不會真的刀槍棍棒、斧鉞鉤叉般討論了,大多數時候都是開玩笑的鬧一鬧。於此同時在總體的互聯網開發中不少時候是一些開發語言公用的,共同打造總體的生態圈。而你們選擇的方式也是更偏向於不一樣領域下選擇適合的架構,而不是一味地追求某個語言。這能夠給不少初學編程的新人一些提議,不要刻意的以爲某個語言好,某個語言很差,只是在適合的場景下選擇最須要的。而你要選擇的那個語言能夠參考招聘網站的需求量和薪資水平決定。node

編程開發不是炫技程序員

總會有人喜歡在總體的項目開發中用上點新特性,把本身新學的知識實踐試試。不能說這樣就是很差,甚至能夠說這是一部分很熱愛學習的人,喜歡創新,喜歡實踐。但編程除了用上新特性外,還須要考慮總體的擴展性、可讀性、可維護、易擴展等方面的考慮。就像你家裏僱傭了一夥裝修師傅,有那麼一個小工喜歡炫技搞花活,在家的淋浴下🚿安裝了馬桶🚽。數據庫

即便是寫CRUD也應該有設計模式編程

每每不少大需求都是經過增刪改查堆出來的,今天要一個需求if一下,明天加個內容else擴展一下。日積月累需求也就愈來愈大,擴展和維護的成本也就愈來愈高。每每大部分研發是不具有產品思惟和總體業務需求導向的,總覺得寫好代碼完成功能便可。但這樣的不考慮擴展性的實現,很難讓後續的需求都快速迭代,長此以往就會被陷入惡性循環,天天都有bug要改。設計模式

2、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三個,能夠經過關注公衆號bugstack蟲洞棧,回覆源碼下載獲取(打開獲取的連接,找到序號18)
工程 描述
itstack-demo-design-8-01 使用一坨代碼實現業務需求
itstack-demo-design-8-02 經過設計模式優化改造代碼,產生對比性從而學習

3、組合模式介紹

組合模式,圖片來自 refactoringguru.cn

從上圖能夠看到這有點像螺絲🔩和螺母,經過一堆的連接組織出一棵結構樹。而這種經過把類似對象(也能夠稱做是方法)組合成一組可被調用的結構樹對象的設計思路叫作組合模式。緩存

這種設計方式可讓你的服務組節點進行自由組合對外提供服務,例如你有三個原子校驗功能(A:身份證B:銀行卡C:手機號)服務並對外提供調用使用。有些調用方須要使用AB組合,有些調用方須要使用到CBA組合,還有一些可能只使用三者中的一個。那麼這個時候你就可使用組合模式進行構建服務,對於不一樣類型的調用方配置不一樣的組織關係樹,而這個樹結構你能夠配置到數據庫中也能夠不斷的經過圖形界面來控制樹結構。微信

因此不一樣的設計模式用在恰當好處的場景可讓代碼邏輯很是清晰並易於擴展,同時也能夠減小團隊新增人員對項目的學習成本。架構

4、案例場景模擬

場景模式;營銷決策樹

以上是一個很是簡化版的營銷規則決策樹,根據性別年齡來發放不一樣類型的優惠券,來刺激消費起到精準用戶促活的目的。

雖然一部分小夥伴可能並無開發過營銷場景,但你可能時時刻刻的被營銷着。好比你去常常瀏覽男性喜歡的機械鍵盤、筆記本電腦、汽車裝飾等等,那麼久給你推薦此類的優惠券刺激你消費。那麼若是你購物很少,或者錢不在本身手裏。那麼你是否打過車,有一段時間常常有小夥伴喊,爲何一樣的距離他就10元,我就15元呢?其實這些都是被營銷的案例,通常對於不常使用軟件的小夥伴,常常會進行稍微大力度的促活,增長用戶粘性。

那麼在這裏咱們就模擬一個相似的決策場景,體現出組合模式在其中起到的重要性。另外,組合模式不僅是能夠運用於規則決策樹,還能夠作服務包裝將不一樣的接口進行組合配置,對外提供服務能力,減小開發成本。

5、用一坨坨代碼實現

這裏咱們舉一個關於ifelse誕生的例子,介紹小姐姐與程序員👨‍💻‍之間的故事致使的事故

日期 需求 緊急程度 程序員(話外音)
星期一.早上 猿哥哥,老闆說要搞一下營銷拉拉量,給男生女生髮不一樣的優惠券,促活消費。 很緊急,下班就要 行吧,也不難,加下判斷就上線
星期二.下午 小哥哥,我們上線後很是好。要讓我們按照年輕、中年、成年,不一樣年齡加下判斷,準確刺激消費。 超緊急,明天就要 也不難,加就加吧
星期三.晚上 喂,小哥哥!睡了嗎!老闆說我們此次活動很成功,能夠不能夠在細分下,把單身、結婚、有娃的都加上不一樣判斷。這樣更能刺激用戶消費。 賊緊急,最快上線。 已經意識到ifelse愈來愈多了
星期四.凌晨 哇!小哥哥大家太棒了,上的真快。嘻嘻!有個小請求,須要調整下年齡段,由於如今學生處對象的都比較早,有對象的更容易買某某某東西。要改下值!辛苦辛苦! 老闆,在等着呢! 一大片的值要修改,哎!這麼多ifelse
星期五.半夜 歪歪喂!巴巴,壞了,怎麼發的優惠券不對了,有客訴了,不少女生都來投訴。你快看看。老闆,他... (一頭汗),哎,值粘錯位置了! 終究仍是一我的扛下了全部

1. 工程結構

itstack-demo-design-8-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── EngineController.java
  • 公司裏要都是這樣的程序員絕對省下很多成本,根本不要搭建微服務,一個工程搞定全部業務!
  • 但千萬不要這麼幹!酒肉穿腸過,佛祖心中留。世人若學我,如同進魔道。

2. 代碼實現

public class EngineController {

    private Logger logger = LoggerFactory.getLogger(EngineController.class);

    public String process(final String userId, final String userSex, final int userAge) {

        logger.info("ifelse實現方式判斷用戶結果。userId:{} userSex:{} userAge:{}", userId, userSex, userAge);

        if ("man".equals(userSex)) {
            if (userAge < 25) {
                return "果實A";
            }

            if (userAge >= 25) {
                return "果實B";
            }
        }

        if ("woman".equals(userSex)) {
            if (userAge < 25) {
                return "果實C";
            }

            if (userAge >= 25) {
                return "果實D";
            }
        }

        return null;

    }

}
  • 除了咱們說的擴展性和每次的維護之外,這樣的代碼實現起來是最快的。並且從樣子來看也很適合新人理解。
  • 可是我勸你別寫,寫這樣代碼不是被扣績效就是被開除。

3. 測試驗證

3.1 編寫測試類

@Test
public void test_EngineController() {
    EngineController engineController = new EngineController();
    String process = engineController.process("Oli09pLkdjh", "man", 29);
    logger.info("測試結果:{}", process);
}
  • 這裏咱們模擬了一個用戶ID,並傳輸性別:man、年齡:29,咱們的預期結果是:果實B。實際對應業務就是給頭禿的程序員發一張枸杞優惠券

3.2 測試結果

22:10:12.891 [main] INFO  o.i.demo.design.EngineController - ifelse實現方式判斷用戶結果。userId:Oli09pLkdjh userSex:man userAge:29
22:10:12.898 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:果實B

Process finished with exit code 0
  • 從測試結果上看咱們的程序運行正常而且符合預期,只不過實現上並非咱們推薦的。接下來咱們會採用組合模式來優化這部分代碼。

6、組合模式重構代碼

接下來使用組合模式來進行代碼優化,也算是一次很小的重構。

接下來的重構部分代碼改動量相對來講會比較大一些,爲了讓咱們能夠把不一樣類型的決策節點和最終的果實組裝成一棵可被運行的決策樹,須要作適配設計和工廠方法調用,具體會體如今定義接口以及抽象類和初始化配置決策節點(性別年齡)上。建議這部分代碼多閱讀幾回,最好實踐下。

1. 工程結構

itstack-demo-design-8-02
└── src
    ├── main
    │   └── java
    │      └── org.itstack.demo.design.domain
    │          ├── model
    │          │   ├── aggregates
    │          │   │   └── TreeRich.java
    │          │   └── vo
    │          │       ├── EngineResult.java
    │          │       ├── TreeNode.java
    │          │       ├── TreeNodeLink.java    
    │          │       └── TreeRoot.java    
    │          └── service
    │              ├── engine
    │              │   ├── impl    
    │              │   │   └── TreeEngineHandle.java       
    │              │   ├── EngineBase.java 
    │              │   ├── EngineConfig.java       
    │              │   └── IEngine.java    
    │              └── logic
    │                  ├── impl    
    │                  │   ├── LogicFilter.java     
    │                  │   └── LogicFilter.java        
    │                  └── LogicFilter.java    
    └── test
         └── java
             └── org.itstack.demo.design.test
                 └── ApiTest.java

組合模式模型結構

組合模式模型結構

  • 首先能夠看下黑色框框的模擬指導樹結構;11112111112121122,這是一組樹結構的ID,並由節點串聯組合出一棵關係樹樹。
  • 接下來是類圖部分,左側是從LogicFilter開始定義適配的決策過濾器,BaseLogic是對接口的實現,提供最基本的通用方法。UserAgeFilterUserGenerFilter,是兩個具體的實現類用於判斷年齡性別
  • 最後則是對這顆能夠被組織出來的決策樹,進行執行的引擎。一樣定義了引擎接口和基礎的配置,在配置裏面設定了須要的模式決策節點。

    • static {
           logicFilterMap = new ConcurrentHashMap<>();
           logicFilterMap.put("userAge", new UserAgeFilter());
           logicFilterMap.put("userGender", new UserGenderFilter());
      }
  • 接下來會對每個類進行細緻的講解,若是感受沒有讀懂必定是我做者的表述不夠清晰,能夠添加個人微信(fustack)與我交流。

2. 代碼實現

2.1 基礎對象

包路徑 介紹
model.aggregates TreeRich 聚合對象,包含組織樹信息
model.vo EngineResult 決策返回對象信息
model.vo TreeNode 樹節點;子葉節點、果實節點
model.vo TreeNodeLink 樹節點連接鏈路
model.vo TreeRoot 樹根信息
  • 以上這部分簡單介紹,不包含邏輯只是各項必要屬性的get/set,整個源代碼能夠經過關注微信公衆號:bugstack蟲洞棧,回覆源碼下載打開連接獲取。

2.2 樹節點邏輯過濾器接口

public interface LogicFilter {

    /**
     * 邏輯決策器
     *
     * @param matterValue          決策值
     * @param treeNodeLineInfoList 決策節點
     * @return 下一個節點Id
     */
    Long filter(String matterValue, List<TreeNodeLink> treeNodeLineInfoList);

    /**
     * 獲取決策值
     *
     * @param decisionMatter 決策物料
     * @return 決策值
     */
    String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);

}
  • 這一部分定義了適配的通用接口,邏輯決策器、獲取決策值,讓每個提供決策能力的節點都必須實現此接口,保證統一性。

2.3 決策抽象類提供基礎服務

public abstract class BaseLogic implements LogicFilter {

    @Override
    public Long filter(String matterValue, List<TreeNodeLink> treeNodeLinkList) {
        for (TreeNodeLink nodeLine : treeNodeLinkList) {
            if (decisionLogic(matterValue, nodeLine)) return nodeLine.getNodeIdTo();
        }
        return 0L;
    }

    @Override
    public abstract String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);

    private boolean decisionLogic(String matterValue, TreeNodeLink nodeLink) {
        switch (nodeLink.getRuleLimitType()) {
            case 1:
                return matterValue.equals(nodeLink.getRuleLimitValue());
            case 2:
                return Double.parseDouble(matterValue) > Double.parseDouble(nodeLink.getRuleLimitValue());
            case 3:
                return Double.parseDouble(matterValue) < Double.parseDouble(nodeLink.getRuleLimitValue());
            case 4:
                return Double.parseDouble(matterValue) <= Double.parseDouble(nodeLink.getRuleLimitValue());
            case 5:
                return Double.parseDouble(matterValue) >= Double.parseDouble(nodeLink.getRuleLimitValue());
            default:
                return false;
        }
    }

}
  • 在抽象方法中實現了接口方法,同時定義了基本的決策方法;一、二、三、四、5等於、小於、大於、小於等於、大於等於的判斷邏輯。
  • 同時定義了抽象方法,讓每個實現接口的類都必須按照規則提供決策值,這個決策值用於作邏輯比對。

2.4 樹節點邏輯實現類

年齡節點

public class UserAgeFilter extends BaseLogic {

    @Override
    public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
        return decisionMatter.get("age");
    }

}

性別節點

public class UserGenderFilter extends BaseLogic {

    @Override
    public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
        return decisionMatter.get("gender");
    }

}
  • 以上兩個決策邏輯的節點獲取值的方式都很是簡單,只是獲取用戶的入參便可。實際的業務開發能夠從數據庫、RPC接口、緩存運算等各類方式獲取。

2.5 決策引擎接口定義

public interface IEngine {

    EngineResult process(final Long treeId, final String userId, TreeRich treeRich, final Map<String, String> decisionMatter);

}
  • 對於使用方來講也一樣須要定義統一的接口操做,這樣的好處很是方便後續拓展出不一樣類型的決策引擎,也就是能夠建造不一樣的決策工廠。

2.6 決策節點配置

public class EngineConfig {

    static Map<String, LogicFilter> logicFilterMap;

    static {
        logicFilterMap = new ConcurrentHashMap<>();
        logicFilterMap.put("userAge", new UserAgeFilter());
        logicFilterMap.put("userGender", new UserGenderFilter());
    }

    public Map<String, LogicFilter> getLogicFilterMap() {
        return logicFilterMap;
    }

    public void setLogicFilterMap(Map<String, LogicFilter> logicFilterMap) {
        this.logicFilterMap = logicFilterMap;
    }

}
  • 在這裏將可提供服務的決策節點配置到map結構中,對於這樣的map結構能夠抽取到數據庫中,那麼就能夠很是方便的管理。

2.7 基礎決策引擎功能

public abstract class EngineBase extends EngineConfig implements IEngine {

    private Logger logger = LoggerFactory.getLogger(EngineBase.class);

    @Override
    public abstract EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter);

    protected TreeNode engineDecisionMaker(TreeRich treeRich, Long treeId, String userId, Map<String, String> decisionMatter) {
        TreeRoot treeRoot = treeRich.getTreeRoot();
        Map<Long, TreeNode> treeNodeMap = treeRich.getTreeNodeMap();
        // 規則樹根ID
        Long rootNodeId = treeRoot.getTreeRootNodeId();
        TreeNode treeNodeInfo = treeNodeMap.get(rootNodeId);
        //節點類型[NodeType];1子葉、2果實
        while (treeNodeInfo.getNodeType().equals(1)) {
            String ruleKey = treeNodeInfo.getRuleKey();
            LogicFilter logicFilter = logicFilterMap.get(ruleKey);
            String matterValue = logicFilter.matterValue(treeId, userId, decisionMatter);
            Long nextNode = logicFilter.filter(matterValue, treeNodeInfo.getTreeNodeLinkList());
            treeNodeInfo = treeNodeMap.get(nextNode);
            logger.info("決策樹引擎=>{} userId:{} treeId:{} treeNode:{} ruleKey:{} matterValue:{}", treeRoot.getTreeName(), userId, treeId, treeNodeInfo.getTreeNodeId(), ruleKey, matterValue);
        }
        return treeNodeInfo;
    }

}
  • 這裏主要提供決策樹流程的處理過程,有點像經過鏈路的關係(性別年齡)在二叉樹中尋找果實節點的過程。
  • 同時提供一個抽象方法,執行決策流程的方法供外部去作具體的實現。

2.8 決策引擎的實現

public class TreeEngineHandle extends EngineBase {

    @Override
    public EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter) {
        // 決策流程
        TreeNode treeNode = engineDecisionMaker(treeRich, treeId, userId, decisionMatter);
        // 決策結果
        return new EngineResult(userId, treeId, treeNode.getTreeNodeId(), treeNode.getNodeValue());
    }

}
  • 這裏對於決策引擎的實現就很是簡單了,經過傳遞進來的必要信息;決策樹信息、決策物料值,來作具體的樹形結構決策。

3. 測試驗證

3.1 組裝樹關係

@Before
public void init() {
    // 節點:1
    TreeNode treeNode_01 = new TreeNode();
    treeNode_01.setTreeId(10001L);
    treeNode_01.setTreeNodeId(1L);
    treeNode_01.setNodeType(1);
    treeNode_01.setNodeValue(null);
    treeNode_01.setRuleKey("userGender");
    treeNode_01.setRuleDesc("用戶性別[男/女]");
    // 連接:1->11
    TreeNodeLink treeNodeLink_11 = new TreeNodeLink();
    treeNodeLink_11.setNodeIdFrom(1L);
    treeNodeLink_11.setNodeIdTo(11L);
    treeNodeLink_11.setRuleLimitType(1);
    treeNodeLink_11.setRuleLimitValue("man");
    // 連接:1->12
    TreeNodeLink treeNodeLink_12 = new TreeNodeLink();
    treeNodeLink_12.setNodeIdTo(1L);
    treeNodeLink_12.setNodeIdTo(12L);
    treeNodeLink_12.setRuleLimitType(1);
    treeNodeLink_12.setRuleLimitValue("woman");
    List<TreeNodeLink> treeNodeLinkList_1 = new ArrayList<>();
    treeNodeLinkList_1.add(treeNodeLink_11);
    treeNodeLinkList_1.add(treeNodeLink_12);
    treeNode_01.setTreeNodeLinkList(treeNodeLinkList_1);
    // 節點:11
    TreeNode treeNode_11 = new TreeNode();
    treeNode_11.setTreeId(10001L);
    treeNode_11.setTreeNodeId(11L);
    treeNode_11.setNodeType(1);
    treeNode_11.setNodeValue(null);
    treeNode_11.setRuleKey("userAge");
    treeNode_11.setRuleDesc("用戶年齡");
    // 連接:11->111
    TreeNodeLink treeNodeLink_111 = new TreeNodeLink();
    treeNodeLink_111.setNodeIdFrom(11L);
    treeNodeLink_111.setNodeIdTo(111L);
    treeNodeLink_111.setRuleLimitType(3);
    treeNodeLink_111.setRuleLimitValue("25");
    // 連接:11->112
    TreeNodeLink treeNodeLink_112 = new TreeNodeLink();
    treeNodeLink_112.setNodeIdFrom(11L);
    treeNodeLink_112.setNodeIdTo(112L);
    treeNodeLink_112.setRuleLimitType(5);
    treeNodeLink_112.setRuleLimitValue("25");
    List<TreeNodeLink> treeNodeLinkList_11 = new ArrayList<>();
    treeNodeLinkList_11.add(treeNodeLink_111);
    treeNodeLinkList_11.add(treeNodeLink_112);
    treeNode_11.setTreeNodeLinkList(treeNodeLinkList_11);
    // 節點:12
    TreeNode treeNode_12 = new TreeNode();
    treeNode_12.setTreeId(10001L);
    treeNode_12.setTreeNodeId(12L);
    treeNode_12.setNodeType(1);
    treeNode_12.setNodeValue(null);
    treeNode_12.setRuleKey("userAge");
    treeNode_12.setRuleDesc("用戶年齡");
    // 連接:12->121
    TreeNodeLink treeNodeLink_121 = new TreeNodeLink();
    treeNodeLink_121.setNodeIdFrom(12L);
    treeNodeLink_121.setNodeIdTo(121L);
    treeNodeLink_121.setRuleLimitType(3);
    treeNodeLink_121.setRuleLimitValue("25");
    // 連接:12->122
    TreeNodeLink treeNodeLink_122 = new TreeNodeLink();
    treeNodeLink_122.setNodeIdFrom(12L);
    treeNodeLink_122.setNodeIdTo(122L);
    treeNodeLink_122.setRuleLimitType(5);
    treeNodeLink_122.setRuleLimitValue("25");
    List<TreeNodeLink> treeNodeLinkList_12 = new ArrayList<>();
    treeNodeLinkList_12.add(treeNodeLink_121);
    treeNodeLinkList_12.add(treeNodeLink_122);
    treeNode_12.setTreeNodeLinkList(treeNodeLinkList_12);
    // 節點:111
    TreeNode treeNode_111 = new TreeNode();
    treeNode_111.setTreeId(10001L);
    treeNode_111.setTreeNodeId(111L);
    treeNode_111.setNodeType(2);
    treeNode_111.setNodeValue("果實A");
    // 節點:112
    TreeNode treeNode_112 = new TreeNode();
    treeNode_112.setTreeId(10001L);
    treeNode_112.setTreeNodeId(112L);
    treeNode_112.setNodeType(2);
    treeNode_112.setNodeValue("果實B");
    // 節點:121
    TreeNode treeNode_121 = new TreeNode();
    treeNode_121.setTreeId(10001L);
    treeNode_121.setTreeNodeId(121L);
    treeNode_121.setNodeType(2);
    treeNode_121.setNodeValue("果實C");
    // 節點:122
    TreeNode treeNode_122 = new TreeNode();
    treeNode_122.setTreeId(10001L);
    treeNode_122.setTreeNodeId(122L);
    treeNode_122.setNodeType(2);
    treeNode_122.setNodeValue("果實D");
    // 樹根
    TreeRoot treeRoot = new TreeRoot();
    treeRoot.setTreeId(10001L);
    treeRoot.setTreeRootNodeId(1L);
    treeRoot.setTreeName("規則決策樹");
    Map<Long, TreeNode> treeNodeMap = new HashMap<>();
    treeNodeMap.put(1L, treeNode_01);
    treeNodeMap.put(11L, treeNode_11);
    treeNodeMap.put(12L, treeNode_12);
    treeNodeMap.put(111L, treeNode_111);
    treeNodeMap.put(112L, treeNode_112);
    treeNodeMap.put(121L, treeNode_121);
    treeNodeMap.put(122L, treeNode_122);
    treeRich = new TreeRich(treeRoot, treeNodeMap);
}

樹形結構的組織關係

  • 重要,這一部分是組合模式很是重要的使用,在咱們已經建造好的決策樹關係下,能夠建立出樹的各個節點,以及對節點間使用鏈路進行串聯。
  • 及時後續你須要作任何業務的擴展均可以在裏面添加相應的節點,並作動態化的配置。
  • 關於這部分手動組合的方式能夠提取到數據庫中,那麼也就能夠擴展到圖形界面的進行配置操做。

3.2 編寫測試類

@Test
public void test_tree() {
    logger.info("決策樹組合結構信息:\r\n" + JSON.toJSONString(treeRich));
    
    IEngine treeEngineHandle = new TreeEngineHandle();
    Map<String, String> decisionMatter = new HashMap<>();
    decisionMatter.put("gender", "man");
    decisionMatter.put("age", "29");
    
    EngineResult result = treeEngineHandle.process(10001L, "Oli09pLkdjh", treeRich, decisionMatter);
    
    logger.info("測試結果:{}", JSON.toJSONString(result));
}
  • 在這裏提供了調用的經過組織模式建立出來的流程決策樹,調用的時候傳入了決策樹的ID,那麼若是是業務開發中就能夠方便的解耦決策樹與業務的綁定關係,按需傳入決策樹ID便可。
  • 此外入參咱們還提供了須要處理;(man)、年齡(29歲),的參數信息。

3.3 測試結果

23:35:05.711 [main] INFO  o.i.d.d.d.service.engine.EngineBase - 決策樹引擎=>規則決策樹 userId:Oli09pLkdjh treeId:10001 treeNode:11 ruleKey:userGender matterValue:man
23:35:05.712 [main] INFO  o.i.d.d.d.service.engine.EngineBase - 決策樹引擎=>規則決策樹 userId:Oli09pLkdjh treeId:10001 treeNode:112 ruleKey:userAge matterValue:29
23:35:05.715 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"nodeId":112,"nodeValue":"果實B","success":true,"treeId":10001,"userId":"Oli09pLkdjh"}

Process finished with exit code 0
  • 從測試結果上看這與咱們使用ifelse是同樣的,可是目前這與的組合模式設計下,就很是方便後續的拓展和修改。
  • 總體的組織關係框架以及調用決策流程已經搭建完成,若是閱讀到此沒有徹底理解,能夠下載代碼觀察結構並運行調試。

7、總結

  • 從以上的決策樹場景來看,組合模式的主要解決的是一系列簡單邏輯節點或者擴展的複雜邏輯節點在不一樣結構的組織下,對於外部的調用是仍然能夠很是簡單的。
  • 這部分設計模式保證了開閉原則,無需更改模型結構你就能夠提供新的邏輯節點的使用並配合組織出新的關係樹。但若是是一些功能差別化很是大的接口進行包裝就會變得比較困難,但也不是不能很好的處理,只不過須要作一些適配和特定化的開發。
  • 不少時候由於你的極致追求和稍有倔強的工匠精神,即便在面對一樣的業務需求,你能完成出最好的代碼結構和最易於擴展的技術架構。不要被遠不能給你指導提高能力的影響到放棄本身的追求!

8、推薦閱讀

相關文章
相關標籤/搜索