做者:小傅哥
博客:https://bugstack.cnhtml
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
小朋友才作選擇題,成年人我都要
java
頭幾年只要羣裏一問我該學哪一個開發語言,哪一個語言最好。羣裏確定聊的特別火熱,有人支持PHP、有人喊號Java、也有C++和C#。但這幾年開始好像你們並不會真的刀槍棍棒、斧鉞鉤叉
般討論了,大多數時候都是開玩笑的鬧一鬧。於此同時在總體的互聯網開發中不少時候是一些開發語言公用的,共同打造總體的生態圈。而你們選擇的方式也是更偏向於不一樣領域下選擇適合的架構,而不是一味地追求某個語言。這能夠給不少初學編程的新人一些提議,不要刻意的以爲某個語言好,某個語言很差,只是在適合的場景下選擇最須要的。而你要選擇的那個語言能夠參考招聘網站的需求量和薪資水平決定。node
編程開發不是炫技
程序員
總會有人喜歡在總體的項目開發中用上點新特性,把本身新學的知識實踐試試。不能說這樣就是很差,甚至能夠說這是一部分很熱愛學習的人,喜歡創新,喜歡實踐。但編程除了用上新特性外,還須要考慮總體的擴展性、可讀性、可維護、易擴展等方面的考慮。就像你家裏僱傭了一夥裝修師傅,有那麼一個小工喜歡炫技搞花活,在家的淋浴下🚿安裝了馬桶🚽。數據庫
即便是寫CRUD也應該有設計模式
編程
每每不少大需求都是經過增刪改查堆出來的,今天要一個需求if
一下,明天加個內容else
擴展一下。日積月累需求也就愈來愈大,擴展和維護的成本也就愈來愈高。每每大部分研發是不具有產品思惟和總體業務需求導向的,總覺得寫好代碼完成功能便可。但這樣的不考慮擴展性的實現,很難讓後續的需求都快速迭代,長此以往就會被陷入惡性循環,天天都有bug要改。設計模式
bugstack蟲洞棧
,回覆源碼下載
獲取(打開獲取的連接,找到序號18)工程 | 描述 |
---|---|
itstack-demo-design-8-01 | 使用一坨代碼實現業務需求 |
itstack-demo-design-8-02 | 經過設計模式優化改造代碼,產生對比性從而學習 |
從上圖能夠看到這有點像螺絲🔩和螺母,經過一堆的連接組織出一棵結構樹。而這種經過把類似對象(也能夠稱做是方法)組合成一組可被調用的結構樹對象的設計思路叫作組合模式。緩存
這種設計方式可讓你的服務組節點進行自由組合對外提供服務,例如你有三個原子校驗功能(A:身份證
、B:銀行卡
、C:手機號
)服務並對外提供調用使用。有些調用方須要使用AB組合,有些調用方須要使用到CBA組合,還有一些可能只使用三者中的一個。那麼這個時候你就可使用組合模式進行構建服務,對於不一樣類型的調用方配置不一樣的組織關係樹,而這個樹結構你能夠配置到數據庫中也能夠不斷的經過圖形界面來控制樹結構。微信
因此不一樣的設計模式用在恰當好處的場景可讓代碼邏輯很是清晰並易於擴展,同時也能夠減小團隊新增人員對項目的學習成本。架構
以上是一個很是簡化版的營銷規則決策樹
,根據性別
、年齡
來發放不一樣類型的優惠券,來刺激消費起到精準用戶促活的目的。
雖然一部分小夥伴可能並無開發過營銷場景,但你可能時時刻刻的被營銷着。好比你去常常瀏覽男性喜歡的機械鍵盤、筆記本電腦、汽車裝飾等等,那麼久給你推薦此類的優惠券刺激你消費。那麼若是你購物很少,或者錢不在本身手裏。那麼你是否打過車,有一段時間常常有小夥伴喊,爲何一樣的距離他就10元,我就15元呢?其實這些都是被營銷的案例,通常對於不常使用軟件的小夥伴,常常會進行稍微大力度的促活,增長用戶粘性。
那麼在這裏咱們就模擬一個相似的決策場景,體現出組合模式在其中起到的重要性。另外,組合模式不僅是能夠運用於規則決策樹,還能夠作服務包裝將不一樣的接口進行組合配置,對外提供服務能力,減小開發成本。
這裏咱們舉一個關於ifelse
誕生的例子,介紹小姐姐與程序員👨💻之間的故事
致使的事故
。
日期 | 需求 | 緊急程度 | 程序員(話外音) |
---|---|---|---|
星期一.早上 | 猿哥哥,老闆說要搞一下營銷拉拉量,給男生女生髮不一樣的優惠券,促活消費。 | 很緊急,下班就要 | 行吧,也不難,加下判斷就上線 |
星期二.下午 | 小哥哥,我們上線後很是好。要讓我們按照年輕、中年、成年,不一樣年齡加下判斷,準確刺激消費。 | 超緊急,明天就要 | 也不難,加就加吧 |
星期三.晚上 | 喂,小哥哥!睡了嗎!老闆說我們此次活動很成功,能夠不能夠在細分下,把單身、結婚、有娃的都加上不一樣判斷。這樣更能刺激用戶消費。 | 賊緊急,最快上線。 | 已經意識到ifelse 愈來愈多了 |
星期四.凌晨 | 哇!小哥哥大家太棒了,上的真快。嘻嘻!有個小請求,須要調整下年齡段,由於如今學生處對象的都比較早,有對象的更容易買某某某東西。要改下值!辛苦辛苦! | 老闆,在等着呢! | 一大片的值要修改,哎!這麼多ifelse 了 |
星期五.半夜 | 歪歪喂!巴巴,壞了,怎麼發的優惠券不對了,有客訴了,不少女生都來投訴。你快看看。老闆,他... | (一頭汗),哎,值粘錯位置了! | 終究仍是一我的扛下了全部 |
itstack-demo-design-8-01 └── src └── main └── java └── org.itstack.demo.design └── EngineController.java
酒肉穿腸過,佛祖心中留。世人若學我,如同進魔道。
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; } }
我勸你別寫
,寫這樣代碼不是被扣績效就是被開除。@Test public void test_EngineController() { EngineController engineController = new EngineController(); String process = engineController.process("Oli09pLkdjh", "man", 29); logger.info("測試結果:{}", process); }
頭禿的程序員發一張枸杞優惠券
。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
組合模式
來優化這部分代碼。接下來使用組合模式來進行代碼優化,也算是一次很小的重構。
接下來的重構部分代碼改動量相對來講會比較大一些,爲了讓咱們能夠把不一樣類型的決策節點和最終的果實組裝成一棵可被運行的決策樹,須要作適配設計和工廠方法調用,具體會體如今定義接口以及抽象類和初始化配置決策節點(性別
、年齡
)上。建議這部分代碼多閱讀幾回,最好實踐下。
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
組合模式模型結構
1
、11
、12
、111
、112
、121
、122
,這是一組樹結構的ID,並由節點串聯組合出一棵關係樹樹。LogicFilter
開始定義適配的決策過濾器,BaseLogic
是對接口的實現,提供最基本的通用方法。UserAgeFilter
、UserGenerFilter
,是兩個具體的實現類用於判斷年齡
和性別
。最後則是對這顆能夠被組織出來的決策樹,進行執行的引擎。一樣定義了引擎接口和基礎的配置,在配置裏面設定了須要的模式決策節點。
static { logicFilterMap = new ConcurrentHashMap<>(); logicFilterMap.put("userAge", new UserAgeFilter()); logicFilterMap.put("userGender", new UserGenderFilter()); }
包路徑 | 類 | 介紹 |
---|---|---|
model.aggregates | TreeRich | 聚合對象,包含組織樹信息 |
model.vo | EngineResult | 決策返回對象信息 |
model.vo | TreeNode | 樹節點;子葉節點、果實節點 |
model.vo | TreeNodeLink | 樹節點連接鏈路 |
model.vo | TreeRoot | 樹根信息 |
get/set
,整個源代碼能夠經過關注微信公衆號:bugstack蟲洞棧
,回覆源碼下載打開連接獲取。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); }
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
,等於、小於、大於、小於等於、大於等於
的判斷邏輯。決策值
,這個決策值用於作邏輯比對。年齡節點
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"); } }
public interface IEngine { EngineResult process(final Long treeId, final String userId, TreeRich treeRich, final Map<String, String> decisionMatter); }
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
結構能夠抽取到數據庫中,那麼就能夠很是方便的管理。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; } }
性別
、年齡
)在二叉樹中尋找果實節點的過程。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()); } }
@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); }
@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)); }
男
(man)、年齡
(29歲),的參數信息。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
是同樣的,可是目前這與的組合模式設計下,就很是方便後續的拓展和修改。不要被遠不能給你指導提高能力的影響到放棄本身的追求!
。