(二十)職責鏈模式詳解(都市異能版)
作者:zuoxiaolong8810(左瀟龍),轉載請註明出處,特別說明:本博文來自博主原部落格,為保證新部落格中博文的完整性,特複製到此留存,如需轉載請註明新部落格地址即可。
凌晨兩點,魔都某出租屋。
"God like."
.......
"Holy shit."
......
“哈哈。老子終於超神一次啦。”伴隨著低音炮中一聲怒吼,小左在自己十來平米的出租房裡興奮的尖叫起來。近了一看,原來小左操作的J8臉30分鐘已經一身神裝,如今正在瘋狂的大殺四方。
“今晚殺的真爽,乾脆通宵得了。”
依依不捨的退出了超神的一局,小左摸了摸自己像漏氣了一樣的肚子,喃喃的說道:“肚子有點餓了啊,這大半夜的可怎麼辦呢。”
“喂,你好,請問現在還送餐嗎?”
“不好意思,先生,您的住址離我們太遠,請您問一下其它分店。”
“問你妹啊,老子問了好幾家了。”心裡雖然不爽,小左嘴上還是很客氣的說道:“嗯,好的,謝謝啊。”
.......
“喂,你好,請問你們往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);
}
}
“現在有了分店,直接向管理部門註冊一下就好了,到時候有訂單就會自動由管理部門一次分發下去了。這一次輸出的結果和第一次應該也是一樣的。這一下訂餐就更方便了,我只管記著麥當勞訂餐部門的電話就行,至於它到底有幾個分店,我就完全不用管啦。”
“下面我就總結一下職責鏈的好處吧,到時候給麥當勞老總打個電話描述一下,哈哈。”
用了職責鏈模式之後,主要的好處是下面兩點。
1、不再需要記憶所有分店的號碼和聯絡方式,然後一個一個去訂餐。
2、不需要知道麥當勞的內部管理結構,正因為這樣,麥當勞再開多少分店,訂餐的客戶都不需要關心,而按照以前的方式,麥當勞每多開一個分店,客戶都有可能需要多記憶一個分店的聯絡方式。
用專業點的語言來說,就是下面兩點。
1、客戶端與具體的處理者解耦,客戶端只認識一個Hanlder介面,降低了客戶端(即請求傳送者)與處理者的耦合度。
2、客戶端和處理者都不關心職責鏈的具體結構,而是交給職責鏈的創造者(在上述例子當中則是交給了OrderManager),也正因為如此,當在職責鏈中新增處理者的時候,這對客戶端和處理者來說,都是透明的,二者不知道也不必要知道職責鏈的變化。
“職責鏈模式的好處應該就是這些啦,下面我來看看剛才的類圖吧。”
“和標準的職責鏈模式類圖幾乎一模一樣,少了一個具體的處理者,不過這並不影響類圖所要傳達的意義。不過這個類圖也有點太簡單了,不過也正因為簡單,從類圖裡一眼就能看出來,職責鏈模式的精髓就在於那一條神奇的聚合線啊。”
“叮咚...”
“咦?我訂的餐到啦,哈哈。”
版權宣告
作者:zuoxiaolong(左瀟龍)
出處:部落格園左瀟龍的技術部落格--http://www.cnblogs.com/zuoxiaolong
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。