1. 程式人生 > >【設計模式】行為型 - 職責鏈

【設計模式】行為型 - 職責鏈

【前提 & 場景】

       寫業務邏輯的時候(和銀行互動或者金融相關業務),小編常會遇到這種情況,如圖:

       

        流程比較複雜,訂單處理節點會很多,按照傳統的設計,程式碼的條理性、可讀性都會很差,經常會出現很多if else判斷,不去好好設計,很容易被後人視為“壞味道”  。而“職責鏈模式”恰恰能很好解決這個問題,詳情往下看。

 

【簡介】

     1.概念

      職責鏈 - 使多個物件(處理者)都有機會處理請求,從而減緩了請求的傳送者和(每個)接受者之間的耦合關係。將這個(處理者)物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理他為止。

     2.UML

      

 

【理解】

      1)場景:1.業務流程非常複雜,2.通常一個請求者,多個處理者,處理過程有順序。

      2)通俗來講,職責鏈模式,就是把處理者重新做了一層封裝,因為處理物件的有序性需求,Handler的實現往往有2個必要的方法,其一是對處理者的節點順序處理,當前節點處理完成之後使其能夠到下一個節點處理。其二是處理當前節點所需要的業務邏輯。

 

【Demo】

      根據現實生活中“考駕照”的需求,寫了一個小demo,規則:

      1.考駕照只能按照順序來,考完一門考下一門。

      2.沒門考試通過生成隨機數來判別考試是否通過,大於60分則通過,小於則繼續從該節點繼續考試。

      1)uml展示:

         

 

     2) 程式碼展示:

     1.流程抽象類

package chainFcar;
/**
 * 模擬處理考試流程
 * 規則:
 * 1.考生只有依次考過了:科目一、科目二、科目三、科目四,方可拿到駕照
 * 2.各次考試考過與否成績均存檔
 * 3.中途未通過考試,下次從上次考試節點繼續考試
 * 
 * @author zhenhua.zhang
 */
public abstract class AbsExamHandler {
	
	/**
	 * 公共方法,每個節點都會執行,子類不用Override
	 */
	protected void commonMethod(){
		//todo 公共方法:eg ->
		//1.通過id查詢考生資訊
		//2.核對考生准考證、身份證等資訊
		//3.and so on  這塊可以拆分多個protected方法
	}
	
	/**
	 * 不同流程的業務起點
	 */
	public abstract void handle(Object o, VincentChain chain);
	
	/**
	 * 不同流程下的業務規則
	 * @return
	 */
	public abstract Boolean businessCheck();
	
	protected void logBeforeExam(Integer type){
		System.out.println("Vincent你的考試科目" + type + "的考試馬上開始!");
	}
	
	protected void logDuringExam(Integer type,Double score){
		System.out.println("Vincent你的考試科目" + type + "的成績是:" + score + "分");
	}
	
	protected void logPassExam(Integer type){
		if(type == 4){
			System.out.println("Vincent,你通過了科目" + type + "的考核,恭喜你拿到了駕駛證!");
			return;
		}
		System.out.println("Vincent,你通過了科目" + type + "的考核,接下來好好準備下一門科目" + (type+1) + "的考核吧!");
	}
	
	protected void logFailExam(Integer type){
		System.out.println("Vincent,你未通過了科目" + type + "的考核,請再接再厲!!!");
	}
	
}

       2.科目一~四實現類

package chainFcar;

/**
 * 科目一考試
 * @author zhenhua.zhang
 *
 */
public class ProjectOneExamHandler extends AbsExamHandler{
	
	@Override
	public void handle(Object o, VincentChain chain){
		//todo
		if(businessCheck()){
			chain.doChain(o);
		}
	}
	
	@Override
	public Boolean businessCheck(){
		logBeforeExam(1);
		//生成1~100之間隨機數,代表考試成績,成績大於60,可以考下一門考試
		Double score = (Double)(Math.random()*100);
		logDuringExam(1,score);
		
		if(score >= 60){
			logPassExam(1);
			return true;
		}
		logFailExam(1);
		return false;
	}
}
package chainFcar;
/**
 * 科目二考試
 * @author zhenhua.zhang
 *
 */
public class ProjectTwoExamHandler extends AbsExamHandler{
	
	@Override
	public void handle(Object o, VincentChain chain){
		//todo
		if(businessCheck()){
			chain.doChain(o);
		}
	}
	
	@Override
	public Boolean businessCheck(){
		logBeforeExam(2);
		//生成1~100之間隨機數,代表考試成績,成績大於60,可以考下一門考試
		Double score = (Double)(Math.random()*100);
		logDuringExam(2,score);
		
		if(score >= 60){
			logPassExam(2);
			return true;
		}
		
		logFailExam(2);
		return false;
	}
}
package chainFcar;

/*
 * 科目一考試
 * @author zhenhua.zhang
 */
public class ProjectThreeExamHandler extends AbsExamHandler{
	
	@Override
	public void handle(Object o, VincentChain chain){
		//todo
		if(businessCheck()){
			chain.doChain(o);
		}
	}
	
	@Override
	public Boolean businessCheck(){
		logBeforeExam(3);
		//生成1~100之間隨機數,代表考試成績,成績大於60,可以考下一門考試
		Double score = (Double)(Math.random()*100);
		logDuringExam(3,score);
		
		if(score >= 60){
			logPassExam(3);
			return true;
		}
		
		logFailExam(3);
		return false;
	}
}
package chainFcar;

/**
 * 科目四考試
 * @author zhenhua.zhang
 *
 */
public class ProjectFourExamHandler extends AbsExamHandler{
	@Override
	public void handle(Object o, VincentChain chain){
		//todo other
		if(businessCheck()){
			chain.doChain(o);
		}
	}
	
	@Override
	public Boolean businessCheck(){
		logBeforeExam(4);
		Double score = (Double)(Math.random()*100);
		logDuringExam(4,score);
		
		if(score >= 60){
			logPassExam(4);
			return true;
		}
		
		logFailExam(4);
		return false;
	}
}

    3.職責鏈分發、流程管理類

package chainFcar;

import java.util.ArrayList;
import java.util.List;

/**
 * 職責鏈分發,流程管理
 * @author zhenhua.zhang
 *
 */
public class VincentChain {
	//處理流程handler集合,利用List有序特性
	List<AbsExamHandler> handler = new ArrayList<AbsExamHandler>();
	
	//index標識位,用於索引handler
	int index = 0;
	
	/**
	 * 按順序向handler中新增元素
	 * @param absHandler
	 * @return
	 */
	public void addHandler(AbsExamHandler absHandler){
		handler.add(absHandler);
		//return this;
	}
	
	/**
	 * 呼叫AbsExamHandler流程
	 */
	public void doChain(Object o){
		if(handler.size() == index){
			return;
		}
		AbsExamHandler handlerNew = handler.get(index);
		index++;
		handlerNew.handle(o,this);
	}
}

4.客戶端啟動訪問類

package chainFcar;

/**
 * 
 * @author zhenhua.zhang
 *
 */

public class VincentClient {
	
	public static void main(String[] args){
		VincentChain chain = new VincentChain();
		ProjectOneExamHandler one = new ProjectOneExamHandler();
		ProjectTwoExamHandler two = new ProjectTwoExamHandler();
		ProjectThreeExamHandler three = new ProjectThreeExamHandler();
		ProjectFourExamHandler four = new ProjectFourExamHandler();
		
		chain.addHandler(one);
		chain.addHandler(two);
		chain.addHandler(three);
		chain.addHandler(four);
		
		Object o = new Object();
		chain.doChain(o);
	}
	
}

    執行結果:

    

      如圖,因為考科目二的時候,隨機數生成的成績只有43.72分,所以到該節點便終止了考試流程。

 

【小結】

      一.上述例子已經展示了職責鏈模式的使用,關鍵點在於有序,這裡用到了List<AbsExamHandler>的有序性保證順序執行。

      二.優缺點比較:

      優點:

      1.長流程保證程式碼整潔、清晰,流程節點的順序能仍能很好保證。

      缺點:

      1.(處理者)在實際的處理中,如果跳過了,就並沒有發揮任何的作用。那麼當這個鏈結構比較長,比較複雜的話,會產生很多的記憶體垃圾物件。這也就是職責鏈的最大缺點之所在。

       2.可能不容易觀察執行時的特徵,有礙於除錯。

      三、對比連結串列

      1. 連結串列是一個鏈狀結構,每個節點有一個next屬性去指向他的下一節點。
      2. 連結串列有一個Header節點,然後使用者每次必須通過頭節點,然後去遍歷尋找每一個節點。
      可見,職責鏈相比連結串列,每次走流程,無需從頭節點開始。

    

      That's all.