天天看點

模闆方法模式

1.模闆方法模式是什麼

1.百度百科

定義一個操作中的算法骨架,而将一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟

2.維基百科

In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses.[1] It lets one redefine certain steps of an algorithm without changing the algorithm's structure.[2]

3.lz了解

将算法中不變的部分放到抽象方法中,變化的部分延遲到子類實作。這樣可以穩定算法中的整體流程,而變動的地方可以任意修改。

4.核心角色

抽象模闆(Abstract Template):抽象模闆是一個抽象類。抽象模闆定義了若幹個方法以表示算法的各個步驟,這些方法中有抽象方法也有非抽象方法,其中的抽象方法稱為原語操作(Primitive Operation)。重要的一點是,抽象模闆中還定義了一個稱之為模闆方法的方法,該方法不僅包含有抽象模闆中表示算法步驟的方法調用,而且也可以包含有定義在抽象模闆中的其他對象的方法調用,即模闆方法定義了算法的骨架。

具體模闆(Concrete Template):具體模闆是抽象模闆的子類,實作抽象模闆中的原語操作。

2.模闆方法模式解決了什麼問題

封裝不變部分,擴充可變部分 可以通過在抽象模闆定義模闆方法給出成熟的算法步驟,同時又不限制步驟的細節,具體模闆實作細節不會改變整個算法的架構。

行為由父類控制,子類實作 在抽象模闆模式中,可以通過鈎子方法對某些步驟進行挂鈎,具體模闆通過鈎子可以選擇算法骨架中的某些步驟。

3.模闆方法模式用法

1.制作飲料

我們就拿泡茶、泡咖啡舉例子。我們需要先燒水然後放入咖啡或者茶葉然後泡好之後加入牛奶或者檸檬片。

抽象模闆

public abstract class MakeDrink {
	//燒水
	protected void boilWater() {
		System.out.println("開始燒水");
	}
	//放入飲品
	protected abstract void put();
	//泡
	protected void brewing() {
		System.out.println("沖泡10min");
	}
	//添加其他
	protected abstract void addOthrers();
  //制作飲料
	public final void makeSomeDrink() {
		System.out.println("開始制作飲料");
		boilWater();
		put();
		brewing();
		addOthrers();
		System.out.println("飲料制作完成");
	}

}

           

具體模闆

public class MakeTea extends MakeDrink {

	@Override
	protected void put() {
		System.out.println("放入茶葉");

	}

	@Override
	protected void addOthrers() {
		System.out.println("放入檸檬片");
	}

}

public class MakeCoffee extends MakeDrink {

	@Override
	protected void put() {
		System.out.println("放入咖啡粉");

	}

	@Override
	protected void addOthrers() {
		System.out.println("添加牛奶");
	}

}
           

用戶端調用

public class Customer {

	public static void main(String[] args) {
		MakeDrink MakeCoffee = new MakeCoffee();
		MakeCoffee.makeSomeDrink();
		MakeDrink MakeTea = new MakeTea();
		MakeTea.makeSomeDrink();
	}

}
           

結果

開始制作飲料

開始燒水

放入咖啡粉

沖泡10min

添加牛奶

飲料制作完成

放入茶葉

放入檸檬片

這時有個問題出現了,如果有人不想要咖啡裡面添加牛奶。或者不想在茶葉裡面添加檸檬。那該怎麼辦呢。

進行如下改造。

在抽象方法中添加一個判斷條件,然後子類重寫他已到達控制是否添加其他原料的目的。

抽象角色模闆

public abstract class MakeDrink {
	//控制是否添加其他的方法
	protected Boolean hasPutOthers() {
		return true;
	}

	protected void boilWater() {
		System.out.println("開始燒水");
	}

	protected abstract void put();

	protected void brewing() {
		System.out.println("沖泡10min");
	}

	protected abstract void addOthrers();

	public final void makeSomeDrink() {
		System.out.println("開始制作飲料");
		boilWater();
		put();
		brewing();
		if(hasPutOthers()) {
			addOthrers();
		}
		System.out.println("飲料制作完成");
	}
}
           

具體方法

public class MakeCoffee extends MakeDrink {

	private Boolean hasOther = true;

	@Override
	protected Boolean hasPutOthers() {
		return hasOther;
	}

	public void setHasOther(Boolean hasOther) {
		this.hasOther = hasOther;
	}

	@Override
	protected void put() {
		System.out.println("放入咖啡粉");

	}

	@Override
	protected void addOthrers() {
		System.out.println("添加牛奶");
	}

}

           
public class Customer {

	public static void main(String[] args) {
		MakeCoffee makeCoffee = new MakeCoffee();
		//1号顧客需要加牛奶
		makeCoffee.setHasOther(true);
		makeCoffee.makeSomeDrink();

		//2号顧客不需要加牛奶
		makeCoffee.setHasOther(false);
		makeCoffee.makeSomeDrink();

	}

}
           

第一個顧客

第二個顧客

寫了這麼多虛的我們還是需要點實際的東西來印證下

2.網際網路金融三方支付網關設計

在網際網路金融系統中ip黑名單檢查、參數簽名驗證、參數完整性驗證、身份證加密解密等工作都是所有請求通用的,隻有到具體的業務操作才會執行不同的業務邏輯.

這時我們建立一個基礎服務類将共有的服務抽取出來。

抽象模闆角色

public abstract class BaseService {

	public final ResResult<String> service(ResVO vo) {
		// 解密參數
		ResResult<ResVO> res = decrypt(vo);
		if (!res.isSuccess()) {
			return new ResResult<String>(res.getCode(),res.getMsg(),"");
		}
		//解密後的參數
		vo = res.getData();
		//ip黑名單驗證
		res = blackListValidate(vo.getIp());
		if(!res.isSuccess()) {
			return new ResResult<String>(res.getCode(),res.getMsg(),"");
		}
		//驗證簽名
		res = verifySign(vo);
		if(!res.isSuccess()) {
			return new ResResult<String>(res.getCode(),res.getMsg(),"");
		}
		//加密敏感内容
		res = encrypt(vo);
		if(!res.isSuccess()) {
			return new ResResult<String>(res.getCode(),res.getMsg(),"");
		}
		// 執行業務邏輯
		res = doBiz(vo);
		return new ResResult<String>(res.getCode(),res.getMsg(),"");
	}

	/**
	 * 業務實作
	 */
	protected abstract ResResult<ResVO> doBiz(ResVO vo);

	/**
	 * 解密參數
	 */
	protected ResResult<ResVO> decrypt(ResVO vo) {
		// ...
		ResResult<ResVO> resResult = new ResResult<ResVO>("0000", "解密完成!", vo);
		return resResult;
	}

	/**
	 * 參數驗證
	 */
	protected ResResult<ResVO> validate(ResVO vo) {
		// ...
		return new ResResult<ResVO>("0000", "驗證完成!", null);
	}

	/**
	 * 黑名單驗證
	 */
	protected ResResult<ResVO> blackListValidate(String ip) {
		// ...
		return new ResResult<ResVO>("0000", "不是黑名單", null);
	}

	/**
	 * 内容加密
	 */
	protected ResResult<ResVO> encrypt(ResVO vo) {
		// ...
		ResResult<ResVO> resResult = new ResResult<ResVO>("0000", "加密完成!", vo);
		return resResult;
	}

	/**
	 * 驗簽
	 */
	protected ResResult<ResVO> verifySign(ResVO vo) {
		// ...
		return new ResResult<ResVO>("0000", "驗簽通過", null);
	}

}
           
public class QuickPayService extends BaseService {

	@Override
	protected ResResult<ResVO> doBiz(ResVO vo) {
		System.out.println("執行快捷支付業務");
		return new ResResult<ResVO>("0000","",vo);
	}

}
           

實體類

/**
 * 響應結果
 */
public class ResResult<T> {

	private String code;
	private String msg;
	private T data;

	public ResResult (String code,String msg){
		this.code = code;
		this.msg = msg;
	}

	public ResResult (String code,String msg,T data){
		this.code = code;
		this.msg = msg;
		this.data = data;
	}



	public Boolean isSuccess() {
		return "0000".equals(code);
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

}


public class ResVO {

	private String  ip;

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}


}

           

調用用戶端

public class Customer {

	public static void main(String[] args) {
		//建立服務類
		QuickPayService quickPayService = new QuickPayService();
		//傳來的參數
		ResVO vo = new ResVO();
		quickPayService.service(vo);
	}
}
           

4.模闆方法模式的問題

模闆方法變更影響巨大 模闆方法模式最基本的功能是通過模闆的制定,把模闆方法完全固定下來。事實上模闆和子類是非常耦合的,如果要對模闆中的模闆方法進行變更,可能要求所有相關的子類進行相應的變化。是以抽取模闆時應盡量確定是不會變化的部分才放到模闆中。

調用反常識 按照設計習慣,抽象類負責聲明最抽象、最一般的事物屬性和方法,實作類負責完成具體的事務屬性和方法,但是模闆方式正好相反,子類執行的結果影響了父類的結果,會增加代碼閱讀的難度。

5.模闆方法模式總結

應用場景:

1.計者需要給出一個算法的固定步驟,并将某些步驟的具體實作留給子類來實作。

2.需要對代碼進行重構,将各個子類公共行為提取出來集中到一個共同的父類中以避免代碼重複。

3.需要控制子類擴充的情況。模闆方法模式會在特定的點來調用子類的方法,這樣隻允許在這些點進行擴充。

如果23種設計模式你隻想學習一個,那麼就學習模闆方法模式吧。這種将算法中變化的部分抽象出來。與不變的部分組成了模闆方法。當調用的時候隻需自己實作自己變化的部分進而達到共用代碼穩定算法結構的目的

上一篇: 橋接模式
下一篇: 裝飾器模式