(二十)職責鏈模式詳解(都市異能版)

    做者: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),也正由於如此,當在職責鏈中添加處理者的時候,這對客戶端和處理者來講,都是透明的,兩者不知道也沒必要要知道職責鏈的變化。

    「職責鏈模式的好處應該就是這些啦,下面我來看看剛纔的類圖吧。」

    「和標準的職責鏈模式類圖幾乎如出一轍,少了一個具體的處理者,不過這並不影響類圖所要傳達的意義。不過這個類圖也有點太簡單了,不過也正由於簡單,從類圖裏一眼就能看出來,職責鏈模式的精髓就在於那一條神奇的聚合線啊。」


    「叮咚...」

    「咦?我訂的餐到啦,哈哈。」

相關文章
相關標籤/搜索