設計模式系列之「責任鏈模式」

「長安回望繡成堆,山頂千門次第開。一騎紅塵妃子笑,無人知是荔枝來」。杜牧的《過華清宮》流傳千古,楊貴妃喜食荔枝也成爲衆所周知的事情。楊貴妃吃的荔枝必須在採摘後的幾天內送到,若是超過了四五天,荔枝就會腐爛,在古代路途遙遠和交通不便成爲了致命傷,爲了讓楊貴妃吃到新鮮的荔枝而不勞民傷財,小Y決定讓楊貴妃和現代物流來一個偶遇。ide

楊貴妃: 大王,臣妾想吃新鮮可口的荔枝(一陣撒嬌聲,各位各自想象)。性能

唐玄宗: 「ok!No趴笨」。而後立刻召來路人甲,讓路人甲不管如何都要在三天以內把荔枝送來,否則以死謝罪。this

朝中大臣紛紛幸災樂禍,以爲路人甲確定會死無葬身之地的。可是路人甲成竹在胸的領命而去 (路人甲之因此這麼信心十足,是由於路人甲曾得神祕高人Y指點,說命中必有一劫,只有按照幾千年後的現代作法建一套有效的物流方法方可避過此劫,因此路人甲就按照神祕高人Y指點祕密在全國各地建造一個物流據點,而後經過特製的千里箭進行通信)spa

回到家中的路人甲裏面把荔枝的貨物需求綁在千里箭中並射向了最近的據點bj倉,bj倉的負責人收到以後,發現這個倉中沒有,而後又把這個需求經過千里箭射向了js倉,但最終在gd倉找到了貨物,再經過特製的飛鷂在三天內把貨物送到楊貴妃所在的地方。設計

楊貴妃吃到了新鮮的荔枝以後,心情大悅,路人甲也所以仕途暢通無阻。調試

本故事終,故事純屬小Y瞎掰,哇哈哈哈。code

1、路人甲如何創建行之有效的物流系統

神祕高人Y把幾千年後的發達物流模式傳授給了路人甲,要求在全國各大城市創建據點。cdn

1.粗糙期

①荔枝的接口對象

public interface ILiZhi {
	//獲得荔枝的品種
	public int getType();
	//每一個倉庫中獲得荔枝訂單的請求
	public String getRequest();
}
複製代碼

②定義荔枝的具體類blog

public class LiZhi implements ILiZhi {

	//荔枝的種類,1表明糯米餈 2表明桂味 3表明槐味
	private int type=0;
	//荔枝的訂單需求
	private String request;

	public LiZhi(int type,String request) {
		this.type = type;
		this.request=request;
	}

	@Override
	public int getType() {
		return type;
	}

	@Override
	public String getRequest() {
		return request;
	}
}
複製代碼

③定義一個據點接口

public interface ILiZhiHouse {
	//處理訂單需求
	public void dealWithRequest(ILiZhi iLiZhi);
}
複製代碼

④bj據點,只有糯米餈荔枝

public class BJHouse implements ILiZhiHouse {
	@Override
	public void dealWithRequest(ILiZhi iLiZhi) {
		System.out.println("訂單需求:"+iLiZhi.getRequest());
		System.out.println("貨物已由bj據點火速送出");
	}
}
複製代碼

⑤js據點,只有桂味荔枝

public class JSHouse implements ILiZhiHouse {
	@Override
	public void dealWithRequest(ILiZhi iLiZhi) {
	System.out.println("訂單需求:"+iLiZhi.getRequest());
	System.out.println("貨物已由js據點火速送出");
	}
}
複製代碼

⑥gd據點,只有槐味荔枝

public class GDHouse implements ILiZhiHouse {
	@Override
	public void dealWithRequest(ILiZhi iLiZhi) {
	System.out.println("訂單需求:"+iLiZhi.getRequest());
	System.out.println("貨物已由gd據點火速送出");
	}
}
複製代碼

物流系統建好了,如今就等楊貴妃下達荔枝需求了。

⑦楊貴妃一聲令下,要求三天以內把糯米餈、桂味、槐味所有送達

public class Client {

	public static void main(String[] args) {
		//1表明糯米餈 2表明桂味 3表明槐味

		//路人甲根據楊貴妃的要求組裝需求
		List<ILiZhi> liZhis=new ArrayList<>();
		liZhis.add(new LiZhi(1,"須要糯米餈30斤"));
		liZhis.add(new LiZhi(2,"須要桂味30斤"));
		liZhis.add(new LiZhi(3,"須要槐味30斤"));

		//定義三個據點
		BJHouse bjHouse=new BJHouse();
		JSHouse jsHouse=new JSHouse();
		GDHouse gdHouse=new GDHouse();

		for(ILiZhi liZhi:liZhis){
			if(liZhi.getType()==1){
				bjHouse.dealWithRequest(liZhi);
			}else if(liZhi.getType()==2){
				jsHouse.dealWithRequest(liZhi);
			}else if(liZhi.getType()==3){
				gdHouse.dealWithRequest(liZhi);
			}else{
			System.out.println("沒有創建這樣的據點,只能坐等死了。");
			}
		}
	}
}
複製代碼

⑧運行結果

訂單需求:須要糯米餈30斤
貨物已由bj據點火速送出

訂單需求:須要桂味30斤
貨物已由js據點火速送出

訂單需求:須要槐味30斤
貨物已由gd據點火速送出
複製代碼

這個就是早期路人甲根據神祕高人Y方法創建的一套物流系統,路人甲以爲這套系統已經天衣無縫了,可是給神祕高人Y一看,立馬指出了一下問題:

  • Client的代碼臃腫,不一樣的荔枝品種就須要增長一個判斷,隨着品種的增多會形成if...else的判斷愈來愈多,很容易出現混亂,可讀性不強。

  • 耦合太重,違背開閉原則。

  • 異常處理不合理。若是路人甲把糯米餈的訂單直接發到了配送桂味的據點,那麼桂味據點沒法處理這個訂單呀,不處理的話那麼路人甲就要坐等死了。

路人甲一看分析出這麼多問題,立馬根據需求設計了一個新的物流系統:訂單下來,由近到遠的據點一個個分配訂單,好比說桂味訂單到了bj據點,bj據點處理不了,只能把這個訂單繼續往下一個據點傳遞,到達js據點可以處理就進行配送,不須要再外下一個據點進行傳遞了,即必然有一個惟一的據點給出惟一的答覆

2.完善期

(1)據點UML

(2)改進後的物流系統代碼

①荔枝的接口

public interface ILiZhi {
	//獲得荔枝的品種
	public int getType();
	//每一個倉庫中獲得荔枝訂單的請求
	public String getRequest();
}
複製代碼

②定義荔枝的具體類

public class LiZhi implements ILiZhi {

	//荔枝的種類,1表明糯米餈 2表明桂味 3表明槐味
	private int type=0;
	//荔枝的訂單需求
	private String request;

	public LiZhi(int type,String request) {
		this.type = type;
		this.request=request;
	}

	@Override
	public int getType() {
		return type;
	}

	@Override
	public String getRequest() {
		return request;
	}
}
複製代碼

③定義一個抽象據點類

public abstract class ILiZhiHouse {

	//定義三種類型
	public final static int BJ_TYPE_REQUEST = 1;
	public final static int JS_TYPE_REQUEST = 2;
	public final static int GD_TYPE_REQUEST = 3;	

	//責任傳遞,下一我的責任人是誰
	private ILiZhiHouse liZhiHouse;
	//能處理的訂單
	private int type =0;

	public ILiZhiHouse(int type) {
		this.type = type;
	}

	//分發請求
	public void handleMessage(ILiZhi iLiZhi){
		if(iLiZhi.getType()==type){
			this.dealWithRequest(iLiZhi);
		}else{
			if(this.liZhiHouse!=null){
				this.liZhiHouse.handleMessage(iLiZhi);
			}else{
				System.out.println("沒有創建這樣的據點,只能坐等死了。");
			}
		}

	}
	//設置下一個據點是哪一個
	public void setNext(ILiZhiHouse liZhiHouse){
		this.liZhiHouse=liZhiHouse;
	}
	//處理訂單需求
	public abstract void dealWithRequest(ILiZhi iLiZhi);
}
複製代碼

④bj據點,只有糯米餈荔枝

public class BJHouse extends ILiZhiHouse {

	public BJHouse() {
		super(BJ_TYPE_REQUEST);
	}

	@Override
	public void dealWithRequest(ILiZhi iLiZhi) {
		System.out.println("訂單需求:"+iLiZhi.getRequest());
		System.out.println("貨物已由bj據點火速送出");
	}
}
複製代碼

⑤js據點,只有桂味荔枝

public class JSHouse extends ILiZhiHouse {

	public JSHouse() {
		super(JS_TYPE_REQUEST);
	}

	@Override
	public void dealWithRequest(ILiZhi iLiZhi) {
		System.out.println("訂單需求:"+iLiZhi.getRequest());
		System.out.println("獲取已由js據點火速送出");
	}
}
複製代碼

⑥gd據點,只有槐味荔枝

public class GDHouse extends ILiZhiHouse {
	public GDHouse() {
		super(GD_TYPE_REQUEST);
	}

	@Override
	public void dealWithRequest(ILiZhi iLiZhi) {
		System.out.println("訂單需求:"+iLiZhi.getRequest());
		System.out.println("獲取已由gd據點火速送出");
	}
}
複製代碼

⑦配送荔枝

public class Client {

	public static void main(String[] args) {
		//1表明糯米餈 2表明桂味 3表明槐味

		//路人甲根據楊貴妃的要求組裝需求
		List<ILiZhi> liZhis=new ArrayList<>();
		liZhis.add(new LiZhi(ILiZhiHouse.BJ_TYPE_REQUEST,"須要糯米餈30斤"));
		liZhis.add(new LiZhi(ILiZhiHouse.JS_TYPE_REQUEST,"須要桂味30斤"));
		liZhis.add(new LiZhi(ILiZhiHouse.GD_TYPE_REQUEST,"須要槐味30斤"));

		//定義三個據點
		BJHouse bjHouse=new BJHouse();
		JSHouse jsHouse=new JSHouse();
		GDHouse gdHouse=new GDHouse();
		//根據據點遠近設定順序
		bjHouse.setNext(jsHouse);
		jsHouse.setNext(gdHouse);

		for(ILiZhi liZhi:liZhis){
			bjHouse.handleMessage(liZhi);
		}
	}
}
複製代碼

⑧運行結果

訂單需求:須要糯米餈30斤
貨物已由bj據點火速送出

訂單需求:須要桂味30斤
貨物已由js據點火速送出

訂單需求:須要槐味30斤
貨物已由gd據點火速送出
複製代碼

業務調用類Client也不用去作判斷究竟是須要誰去處理,並且ILiZhiHouse抽象類的子類能夠繼續增長下去,只須要擴展傳遞鏈而已,調用類能夠不用瞭解變化過程,甚至是誰在處理這個請求都不用知道。

2、基本概念

1.定義

使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。

2.責任鏈的重點

責任鏈模式的重點是在「鏈」上,由一條鏈去處理類似的請求在鏈中決定誰來處理這個請求,並返回相應的結果。

3.角色介紹

  • Handler
    抽象的處理者實現三個職責:一是定義一個請求的處理方法handleMessage;二是定義一個鏈的編排方法setNext,設置下一個處理者;三是定義了具體的請求者必須實現的兩個方法:定義本身可以處理的級別和具體的處理任務。

  • ConcreteHandler
    責任鏈模式的核心在「鏈」上,「鏈」是由多個處理者ConcreteHandler組成的。具體的請求者必須實現的兩個方法:定義本身可以處理的級別和具體的處理任務。

3、責任鏈模式優缺點

1.優勢

  • 責任鏈模式很是顯著的優勢是將請求和處理分開。請求者能夠不用知道是誰處理的,處理者能夠不用知道請求的全貌,,二者解耦,提升系統的靈活性

2.缺點

  • 性能問題。每一個請求都是從鏈頭遍歷到鏈尾,特別是在鏈比較長的時候,性能是一個很是大的問題

  • 調試不很方便,特別是鏈條比較長,環節比較多的時候,因爲採用了相似遞歸的方式,調試的時候邏輯可能比較複雜。

4、總結

鏈中節點數量須要控制,避免出現超長鏈的狀況,通常的作法是在Handler中設置一個最大節點數量,在setNext方法中判斷是否已是超過其閾值,超過則不容許該鏈創建,避免無心識地破壞系統性能。

Android技術交流吧
相關文章
相關標籤/搜索