做者:zuoxiaolong8810(左瀟龍),轉載請註明出處。 java
凌晨兩點,魔都某出租屋。 設計模式
"God like." 工具
....... 優化
"Holy shit." this
...... spa
「哈哈。老子終於超神一次啦。」伴隨着低音炮中一聲怒吼,小左在本身十來平米的出租房裏興奮的尖叫起來。近了一看,原來小左操做的J8臉30分鐘已經一身神裝,現在正在瘋狂的大殺四方。 設計
「今晚殺的真爽,乾脆通宵得了。」 code
戀戀不捨的退出了超神的一局,小左摸了摸本身像漏氣了同樣的肚子,喃喃的說道:「肚子有點餓了啊,這大半夜的可怎麼辦呢。」 對象
「喂,你好,請問如今還送餐嗎?」 接口
「很差意思,先生,您的住址離咱們太遠,請您問一下其它分店。」
「問你妹啊,老子問了好幾家了。」內心雖然不爽,小左嘴上仍是很客氣的說道:「嗯,好的,謝謝啊。」
.......
「喂,你好,請問大家往XXXX小區送餐嗎?」
「嗯,是的。先生。」
小左心中一樂,「嗯,那好,我要一個XXX,一個XXX,一個XXX。」
「很差意思,先生,如今太晚了,咱們的送餐時間已通過了。」
「我....去....你....妹啊。不送不早說。」心中暗罵了一句,小左直接掛了電話。
.......
「喂,你好,請問大家往XXXX小區送餐嗎?」
「嗯,是的。先生。」
「嗯,那大家送餐時間沒過吧。」有了上次的經驗,小左沒有先報菜單,而是先確認對方是否過了送餐時間。
「沒有,先生。」
「嗯,那好,我要一個XXX,一個XXX,一個XXX。」
「很差意思,先生,這些都已經賣完了,您看您能換別的嗎?」
「我....換你大爺.....」
半個小時過去了...
「哎呀媽呀,累死我了。終於找到一家送餐的了。這麥當勞號稱24小時外賣是坑爹呢吧。」通過將近半個小時的折騰,小左終於找到一家比較合適的麥當勞分店,如今就只等着外賣送過來了。
「外賣估計半個小時就到了,這一會也開不了一局,乾脆乾點別的,等吃完飯再開始。」說着,小左便開始在網上找尋設計模式的內容,這一閒下來就研究設計模式的勁頭,當真是不辱轉生前程序猿的頭銜。
「啊哈,職責連模式。看起來能夠解決麥當勞訂餐的問題啊,好好研究一下,給麥當勞老總提個建議,說不定賞我個幾億美金。哇哈哈。」還沒開始研究,小左已經開始YY起來。
定義:爲了不請求的發送者和接收者之間的耦合關係,使多個接受對象都有機會處理請求。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
「看這個定義,就是將一堆能夠處理請求的對象連成一條鏈,而後一個一個試着處理請求。這好像是能夠解決麥當勞訂餐的問題的,我先來看看我剛纔苦B的訂餐過程是什麼樣子的。」
「首先應該有一個麥當勞的分店的類,它的主要功能是能夠訂餐。」
package com.chain; import java.util.Collections; import java.util.Map; //麥當勞分店 public class McSubbranch { private final static int MIN_DISTANCE = 500;//假設是500米之內送餐 private static int count;//類計數 private final int number;//分店號 private int x;//分店的橫座標,用於判斷距離 private int y;//分店的縱座標,用於判斷距離 private Map<String, Integer> menu;//菜單 public McSubbranch(int x, int y, Map<String, Integer> menu) { super(); this.x = x; this.y = y; this.menu = menu; number = ++count; } public boolean order(int x,int y,Map<String, Integer> order){ //若是距離小於500米而且訂單中的食物不缺貨,則訂單成功,不然失敗 if (CommonUtils.getDistance(x, y, this.x, this.y) < MIN_DISTANCE && !CommonUtils.outOfStock(menu, order)) { for (String name : order.keySet()) { menu.put(name, menu.get(name) - order.get(name)); } return true; }else { return false; } } public Map<String, Integer> getMenu() { return Collections.unmodifiableMap(menu); } public String toString() { return "麥當勞分店第" + number + "個"; } }「這裏面用到了一個工具類,主要是用來計算距離和判斷是否缺貨的,就是這樣寫的。」
package com.chain; import java.util.Map; //簡單的工具類 public class CommonUtils { private CommonUtils(){} //計算座標之間的距離 public static double getDistance(int x1,int y1,int x2,int y2){ return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } //是否缺貨 public static boolean outOfStock(Map<String, Integer> menu,Map<String, Integer> order){ if (order == null || order.size() == 0) { return false; } if (menu == null || menu.size() == 0) { return true; } for (String name : order.keySet()) { if (menu.get(name) - order.get(name) < 0) { return true; } } return false; } }「下面就是我苦B的訂餐過程了,假設有五個分店的話,我訂餐的過程就是一家一家挨着去訂,直到成功爲止。」
package com.chain; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Client { public static void main(String[] args) { //假設初始菜單都是如下這些東西 Map<String, Integer> menu = new HashMap<String, Integer>(); menu.put("漢堡", 5); menu.put("薯條", 5); menu.put("可樂", 5); menu.put("雪碧", 5); //假設有5個分店 McSubbranch mcSubbranch1 = new McSubbranch(0, 0, new HashMap<String, Integer>(menu)); McSubbranch mcSubbranch2 = new McSubbranch(100, 120, new HashMap<String, Integer>(menu)); McSubbranch mcSubbranch3 = new McSubbranch(-100, -120, new HashMap<String, Integer>(menu)); McSubbranch mcSubbranch4 = new McSubbranch(1000, 20, new HashMap<String, Integer>(menu)); McSubbranch mcSubbranch5 = new McSubbranch(-500, 0, new HashMap<String, Integer>(menu)); List<McSubbranch> mcSubbranchs = Arrays.asList(mcSubbranch1,mcSubbranch2,mcSubbranch3,mcSubbranch4,mcSubbranch5); //小左開始訂餐,假設小左的座標是900,20 Map<String, Integer> order = new HashMap<String, Integer>(); order.put("漢堡", 2); order.put("可樂", 1); order.put("薯條", 1); print(mcSubbranchs); System.out.println("------------------------------------------"); //小左開始一家一家挨着嘗試訂餐,直到成功 for (McSubbranch mcSubbranch : mcSubbranchs) { if (mcSubbranch.order(900, 20, order)) { System.out.println("訂餐成功,接受訂單的分店是:" + mcSubbranch); break; } } System.out.println("------------------------------------------"); print(mcSubbranchs); } public static void print(List<McSubbranch> mcSubbranchs){ for (McSubbranch mcSubbranch : mcSubbranchs) { System.out.println("[" + mcSubbranch + "]的菜單:" + mcSubbranch.getMenu()); } } }
「這樣確實比較悲催啊,我得一家一家打電話問,太麻煩了。麥當勞這麼大一個企業,訂餐的服務居然這麼爛哦。看我用設計模式給你優化一下吧,哈哈。」
「先來看看職責鏈模式的類圖,這樣比較好設計。」
「類圖仍是比較簡單的啊,有一個通用的接口,而後就是若干個具體的處理者。按照如今麥當勞的狀況來講,接口裏handleRequest方法其實就是order(訂餐)方法了,而setSuccessor方法,則是用來設置職責鏈的下一個處理者。」
「對於麥當勞的問題來講,每個分店就是具體的處理者了,主要的改動應該是抽象出來一個接口以及職責鏈的鏈接過程,而剛纔發送訂單的時候是拆分紅方法參數傳遞給訂餐方法的,如今最好是把訂單作成一個數據類。」
package com.chain; import java.util.Map; //訂單類(至關於request,其實就是封裝一個請求) public class Order { private int x; private int y; private Map<String, Integer> order; public Order(int x, int y, Map<String, Integer> order) { super(); this.x = x; this.y = y; this.order = order; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public Map<String, Integer> getOrder() { return order; } public void setOrder(Map<String, Integer> order) { this.order = order; } }「下面便應該是分店接口了,它有兩個方法,和類圖當中的方法相似,只是名稱略有改變。」
package com.chain; //分店接口(至關於Hanlder) public interface Subbranch { void setSuccessor(Subbranch subbranch); boolean handleOrder(Order order); }「下面即是麥當勞分店的實現類了,它主要的改變是添加了一個屬性(下一個分店),這應該就是鏈造成的基石了。」
package com.chain; import java.util.Collections; import java.util.Map; //麥當勞分店 public class McSubbranch implements Subbranch{ private final static int MIN_DISTANCE = 500;//假設是500米之內送餐 private static int count;//類計數 private final int number;//分店號 private int x;//分店的橫座標,用於判斷距離 private int y;//分店的縱座標,用於判斷距離 private Map<String, Integer> menu;//菜單 private Subbranch nextSubbranch;//下一家分店 public McSubbranch(int x, int y, Map<String, Integer> menu) { super(); this.x = x; this.y = y; this.menu = menu; number = ++count; } //設置下一家分店 public void setSuccessor(Subbranch subbranch) { this.nextSubbranch = subbranch; } //按照職責鏈處理訂單 public boolean handleOrder(Order order){ //若是距離小於500米而且訂單中的食物不缺貨,則訂單成功,不然失敗 if (CommonUtils.getDistance(order.getX(), order.getY(), this.x, this.y) < MIN_DISTANCE && !CommonUtils.outOfStock(menu, order.getOrder())) { for (String name : order.getOrder().keySet()) { menu.put(name, menu.get(name) - order.getOrder().get(name)); } System.out.println("訂餐成功,接受訂單的分店是:" + this); return true; } if (nextSubbranch == null) { return false; } return nextSubbranch.handleOrder(order); } public Map<String, Integer> getMenu() { return Collections.unmodifiableMap(menu); } public Subbranch getNextSubbranch() { return nextSubbranch; } public String toString() { return "麥當勞分店第" + number + "個"; } }「handleOrder方法中的邏輯就是職責鏈的精髓了,它會試圖處理請求,若是處理不了,則交給鏈中的下一個分店。剛纔用的CommonUtils應該不用變了。下面就看下有了職責鏈模式以後,個人訂餐方式吧。」
package com.chain; import java.util.HashMap; import java.util.Map; public class Client { public static void main(String[] args) { //假設初始菜單都是如下這些東西 Map<String, Integer> menu = new HashMap<String, Integer>(); menu.put("漢堡", 5); menu.put("薯條", 5); menu.put("可樂", 5); menu.put("雪碧", 5); //假設有5個分店 Subbranch mcSubbranch1 = new McSubbranch(0, 0, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch2 = new McSubbranch(100, 120, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch3 = new McSubbranch(-100, -120, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch4 = new McSubbranch(1000, 20, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch5 = new McSubbranch(-500, 0, new HashMap<String, Integer>(menu)); //如下設置職責鏈 mcSubbranch4.setSuccessor(mcSubbranch5); mcSubbranch3.setSuccessor(mcSubbranch4); mcSubbranch2.setSuccessor(mcSubbranch3); mcSubbranch1.setSuccessor(mcSubbranch2); //小左開始訂餐,假設小左的座標是900,20 Map<String, Integer> order = new HashMap<String, Integer>(); order.put("漢堡", 2); order.put("可樂", 1); order.put("薯條", 1); print(mcSubbranch1); System.out.println("------------------------------------------"); //小左開始訂餐,直接找mcSubbranch1的這一家分店訂餐便可 mcSubbranch1.handleOrder(new Order(900, 20, order)); System.out.println("------------------------------------------"); print(mcSubbranch1); } public static void print(Subbranch subbranch){ if (subbranch == null ) { return; } do { if (subbranch instanceof McSubbranch) { System.out.println("[" + subbranch + "]的菜單:" + ((McSubbranch) subbranch).getMenu()); } } while ((subbranch = ((McSubbranch) subbranch).getNextSubbranch()) != null); } }「輸出結果和剛纔是同樣的,不過這下我訂餐就好辦多了,直接找第一家分店訂餐就行,至於到最後誰給我送餐,我就不用管了。」
「職責鏈模式果真強大啊,哈哈。不過如今仍是有一點缺陷,那就是訂餐的時候我還得記着應該找哪家分店,若是麥當勞有一個專門的訂餐管理部門就行了,這樣的話,就更容易找到該找誰訂餐了。下面我就再添加一個訂餐管理部門,完善一下吧。」
package com.chain; //訂餐管理部門,它並非職責鏈模式中的角色 //可是在職責鏈模式中,一般狀況下會有一個類專門負責維護職責鏈 //在本例中,這個類稱爲訂餐管理部門更合適 public class OrderManager { private static OrderManager orderManager = new OrderManager(); private Subbranch head; private Subbranch last; private OrderManager(){} public static OrderManager getOrderManager(){ return orderManager; } //註冊分店 public void registerSubbranch(Subbranch... subbranchs){ for (Subbranch subbranch : subbranchs) { registerSubbranch(subbranch); } } public void registerSubbranch(Subbranch subbranch){ if (head == null) { last = head = subbranch; }else { last.setSuccessor(subbranch); last = subbranch; } } public boolean handleOrder(Order order){ return head.handleOrder(order); } }「其他的都不用變,這下我就使用訂餐管理部門(能夠理解爲麥當勞總店或者總服務檯均可以)訂餐試一下吧。」
package com.chain; import java.util.HashMap; import java.util.Map; public class Client { public static void main(String[] args) { //假設初始菜單都是如下這些東西 Map<String, Integer> menu = new HashMap<String, Integer>(); menu.put("漢堡", 5); menu.put("薯條", 5); menu.put("可樂", 5); menu.put("雪碧", 5); //假設有5個分店 Subbranch mcSubbranch1 = new McSubbranch(0, 0, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch2 = new McSubbranch(100, 120, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch3 = new McSubbranch(-100, -120, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch4 = new McSubbranch(1000, 20, new HashMap<String, Integer>(menu)); Subbranch mcSubbranch5 = new McSubbranch(-500, 0, new HashMap<String, Integer>(menu)); //註冊5個分店 OrderManager.getOrderManager().registerSubbranch(mcSubbranch1,mcSubbranch2,mcSubbranch3,mcSubbranch4,mcSubbranch5); //小左開始訂餐,假設小左的座標是900,20 Map<String, Integer> order = new HashMap<String, Integer>(); order.put("漢堡", 2); order.put("可樂", 1); order.put("薯條", 1); print(mcSubbranch1); System.out.println("------------------------------------------"); //小左開始訂餐,直接找訂餐管理部門訂餐 OrderManager.getOrderManager().handleOrder(new Order(900, 20, order)); System.out.println("------------------------------------------"); print(mcSubbranch1); } public static void print(Subbranch subbranch){ if (subbranch == null ) { return; } do { if (subbranch instanceof McSubbranch) { System.out.println("[" + subbranch + "]的菜單:" + ((McSubbranch) subbranch).getMenu()); } } while ((subbranch = ((McSubbranch) subbranch).getNextSubbranch()) != null); } }「如今有了分店,直接向管理部門註冊一下就行了,到時候有訂單就會自動由管理部門一次分發下去了。這一次輸出的結果和第一次應該也是同樣的。這一下訂餐就更方便了,我只管記着麥當勞訂餐部門的電話就行,至於它到底有幾個分店,我就徹底不用管啦。」
「下面我就總結一下職責鏈的好處吧,到時候給麥當勞老總打個電話描述一下,哈哈。」
用了職責鏈模式以後,主要的好處是下面兩點。
一、再也不須要記憶全部分店的號碼和聯繫方式,而後一個一個去訂餐。
二、不須要知道麥當勞的內部管理結構,正由於這樣,麥當勞再開多少分店,訂餐的客戶都不須要關心,而按照之前的方式,麥當勞每多開一個分店,客戶都有可能須要多記憶一個分店的聯繫方式。
用專業點的語言來講,就是下面兩點。
一、客戶端與具體的處理者解耦,客戶端只認識一個Hanlder接口,下降了客戶端(即請求發送者)與處理者的耦合度。
二、客戶端和處理者都不關心職責鏈的具體結構,而是交給職責鏈的創造者(在上述例子當中則是交給了OrderManager),也正由於如此,當在職責鏈中添加處理者的時候,這對客戶端和處理者來講,都是透明的,兩者不知道也沒必要要知道職責鏈的變化。
「職責鏈模式的好處應該就是這些啦,下面我來看看剛纔的類圖吧。」
「和標準的職責鏈模式類圖幾乎如出一轍,少了一個具體的處理者,不過這並不影響類圖所要傳達的意義。不過這個類圖也有點太簡單了,不過也正由於簡單,從類圖裏一眼就能看出來,職責鏈模式的精髓就在於那一條神奇的聚合線啊。」
「叮咚...」
「咦?我訂的餐到啦,哈哈。」