模板方法模式和策略模式的應用場景淺析


最近閒下來整理一下模板方法模式和策略模式的區別和應用場景

一、定義

模板方法模式:定義一個算法的骨架,將骨架中的特定步驟延遲到子類中。模板方法模式使得子類可以不改變算法的結構即可重新定義該算法的某些特定步驟

再看策略模式:

策略模式:定義一系列的算法,把它們一個個封裝起來, 並且使它們可相互替換

對於熟悉這兩個定義的人來說,一目瞭然(然而當初我學習設計模式的時候,真覺得自己沒學過語文)。換一種表達就是,模板模式注重的是一個骨架,定義一套步驟,1、2、3、4、5…,每個步驟做什麼操作,可以被子類覆蓋,但步驟的執行順序不能改變;而策略模式注重的是操作本身,定義一個操作會在特定時刻調用,但具體的操作可以用子類實現。
應用場景可以看下面的例子,上“栗子”。

二、舉個例子

前段時間跟同事討論了一下魚的做法,引出了代碼實現,這次就舉這個例子,權當需求。

1.殺魚

殺一條魚的順序,殺魚->去魚鱗->清內臟,每一步具體操作都可能不一樣,比如殺魚可以用刀背、擀麪杖等,魚鱗可以用到刮、可以用手拔(我第一次殺魚就是用手拔的魚鱗<-_->),但是整體流程確實幾乎不變的。

2.做魚

一條魚根據烹飪方式不同可以做出很多道菜,可以清蒸、紅燒、炭烤等,每種烹飪方式的過程都不大相同。

三、僞代碼實現

1.殺魚

1.1分析

殺魚的方式有很多,但卻可以認爲有一個固有流程,殺魚->去魚鱗->清內臟,每一個步驟都可以因方式的不同而被替換,但總體流程不變;通過上面的定義可以選擇模板模式實現

1.2實現

  • 定義模板類PrepareFishTemplate
/**
 * 準備食材魚
 */
public abstract class PrepareFishTemplate {

	/**
	 * 清理魚鱗
	 */
	public abstract void clearScales();

	/**
	 * 清理內臟
	 */
	public abstract void clearViscera();

	/**
	 * 殺魚
	 */
	public abstract void kill();

	public final void perpare() {
		this.kill();
		this.clearScales();
		this.clearViscera();
	}

}
  • 每種殺魚方式都需要定義單獨類去繼承模板類,並實現相應方法
    • 常規方式CommonPrepareFish
    /**
     * 常規處理魚
     */
    public class CommonPrepareFish extends PrepareFishTemplate {
    
    	@Override
    	public void clearScales() {
    		System.out.println("使用刀颳去魚鱗...");
    	}
    
    	@Override
    	public void clearViscera() {
    		System.out.println("用手將內臟摳出...");
    	}
    
    	@Override
    	public void kill() {
    		System.out.println("---------開始常規處理魚--------------");
    		System.out.println("把魚用刀背拍死...");
    	}
    
    }
    
    • 新手處理NovicePrepareFish
    /**
     * 新手處理魚
     */
    public class NovicePrepareFish extends PrepareFishTemplate {
    
    	@Override
    	public void clearScales() {
    		System.out.println("用手將魚鱗全部拔出...");
    	}
    
    	@Override
    	public void clearViscera() {
    		System.out.println("用手將內臟摳出...");
    	}
    
    	@Override
    	public void kill() {
    		System.out.println("---------開始新手處理魚--------------");
    		System.out.println("抓着魚摔打到不再反抗...");
    	}
    
    }
    
    • 佛系處理
    /**
     * 佛系處理魚
     */
    public class GodPrepareFish extends PrepareFishTemplate {
    
    	@Override
    	public void clearScales() {
    		System.out.println("試圖說服魚鱗擺脫魚的束縛...");
    	}
    
    	@Override
    	public void clearViscera() {
    		System.out.println("試圖說服內臟自己跑出來...");
    	}
    
    	@Override
    	public void kill() {
    		System.out.println("---------開始佛系處理魚--------------");
    		System.out.println("魚呀,別掙扎了,命運已經註定了(巴拉巴拉到魚不再反抗)...");
    	}
    
    }
    
  • 運行程序
public class Main {

	public static void main(String[] args) {
		PrepareFishTemplate common = new CommonPrepareFish();
		PrepareFishTemplate novice = new NovicePrepareFish();
		PrepareFishTemplate god = new GodPrepareFish();
		common.perpare();
		novice.perpare();
		god.perpare();
	}
}

運行效果如下:

---------開始常規處理魚--------------
把魚用刀背拍死…
使用刀颳去魚鱗…
用手將內臟摳出…
---------開始新手處理魚--------------
抓着魚摔打到不再反抗…
用手將魚鱗全部拔出…
用手將內臟摳出…
---------開始佛系處理魚--------------
魚呀,別掙扎了,命運已經註定了(巴拉巴拉到魚不再反抗)…
試圖說服魚鱗擺脫魚的束縛…
試圖說服內臟自己跑出來…

2.做魚

2.1分析

把魚做成菜就跟殺魚的邏輯不一樣了,每道菜都需要特定的流程操作,步驟不一而定,但整體入口出口一致,即入口需要確定一條魚,出口輸出一道菜。每道菜都用擁有自己的烹飪邏輯,且根據目標菜品來選擇烹飪過程,故在此選擇策略模式。

2.2實現

  • 定義烹飪接口
/**
 * 魚類烹飪接口
 */
public interface CookFishInterface {

	/**
	 * 烹飪
	 */
	public void cook();
}
  • 定義實現類
/**
 * 碳烤魚
 */
public class GrilledFish implements CookFishInterface {

	public void cook() {
		System.out.println("----開始做一道碳烤魚-----");
		System.out.println("碳烤魚的各種步驟...");
		System.out.println("----碳烤魚出爐-----");
	}

}
/**
 * 生魚片
 */
public class Sashimi implements CookFishInterface {

	public void cook() {
		System.out.println("----開始做生魚片-----");
		System.out.println("生魚片的各種步驟...");
		System.out.println("----生魚片做成-----");
	}

}
/**
 * 清蒸魚
 */
public class SteamedFish implements CookFishInterface {

	public void cook() {
		System.out.println("----開始做一道清蒸魚-----");
		System.out.println("清蒸魚的各種步驟...");
		System.out.println("----清蒸魚出爐-----");
	}

}
  • 定義上下文
/**
 * 魚類烹飪上下文
 */
public class FishContext {
	private CookFishInterface cookFishInterface;

	public FishContext(CookFishInterface fishInterface) {
		this.cookFishInterface = fishInterface;
	}

	public void cook() {
		System.out.println("------------開始烹飪----------");
		cookFishInterface.cook();
	}
}
  • 運行代碼
public class Main {
	public static void main(String[] args) {
		FishContext context1 = new FishContext(new GrilledFish());
		context1.cook();
		FishContext context2 = new FishContext(new SteamedFish());
		context2.cook();
		FishContext context3 = new FishContext(new Sashimi());
		context3.cook();
	}
}

輸出結果如下:

------------開始烹飪----------
----開始做一道碳烤魚-----
碳烤魚的各種步驟…
----碳烤魚出爐-----
------------開始烹飪----------
----開始做一道清蒸魚-----
清蒸魚的各種步驟…
----清蒸魚出爐-----
------------開始烹飪----------
----開始做生魚片-----
生魚片的各種步驟…
----生魚片做成-----

四、總結

可以看出,模板模式是將一整套流程提取出來做了封裝,對於流程內的實現可以被替換,而策略模式替換的是整個操作;換句話說,當需要確定一系列不可被更改順序的操作,但其中步驟可以被替換時,選擇策略模式;當需要多種可以被替換的算法實現時,選擇策略模式。

PS:實際開發過程中,一般不會單一模式使用,比如模板模式中每一個步驟都可以是一個策略模式實現;而策略模式的上下文可以使用工廠模式,從而實現外部不知道有哪些策略,但可以根據傳入參數有工廠選擇具體的策略。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章