模板方法模式
含義概述
模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法中的某些步驟。
使用場景
當(dāng)有一個業(yè)務(wù)有 N 個步驟( 模板 ),其中一部分步驟是永恒不變的,那么就將不變的這些步驟抽象到父類中,可能變化的步驟留給子類去實現(xiàn)。
組成元素
- 模板方法:定義算法的整體流程,通常推薦使用 final 關(guān)鍵字修飾,防止子類重寫,從而破壞了模板中規(guī)定好的流程
- 抽象方法:使用 abstract 修飾,讓子類去實現(xiàn)
- 普通方法:父類提供默認(rèn)實現(xiàn),設(shè)置為private則不允許子類也可重寫,設(shè)置為public或者proteced則允許子類重寫,具體訪問修飾符的級別設(shè)置可以根據(jù)需求設(shè)定,不做強(qiáng)制規(guī)定
- 鉤子方法:模板方法中提前預(yù)埋多個鉤子,讓子類有一定的能力影響抽象類中的算法流程。
案例
- Servlet 中的模板方法:doGet()/doPost()/service()方法
- Arrays.sort() 方法,它要求對象實現(xiàn) Comparable 接口
優(yōu)缺點比較
模板方法模式是將子類中不變的部分抽象到父類,可變的部分由子類去實現(xiàn)。
優(yōu)點
封裝不變公共代碼,便于維護(hù)??勺儾糠值拇a由子類自由決定,擴(kuò)展性強(qiáng)。缺點
每新增一個不同的實現(xiàn)都需要增加一個子類,可能導(dǎo)致類數(shù)量變多,增加系統(tǒng)復(fù)雜性。
實例
Cook.java
public abstract class Cook {
/**
* 鉤子的開關(guān)變量
*/
protected boolean needBeforeCook = false;
/**
* 鉤子的開關(guān)
*
* @return
*/
protected boolean needBeforeCook() {
System.out.println("===鉤子函數(shù)被禁用===");
return false;
}
/**
* 鉤子函數(shù)
*/
public void beforeCook() {
}
public void open() {
System.out.println("打開抽油煙機(jī)");
}
public void fire() {
System.out.println("生火");
}
/**
* 期望子類去實現(xiàn)
*/
public abstract void doCook();
public void outfire() {
System.out.println("滅火");
}
public void close() {
System.out.println("關(guān)閉抽油煙機(jī)");
System.out.println("======收工=======");
}
/**
* 使用final關(guān)鍵字,防止子類重寫
*/
public final void cook() {
// 默認(rèn)開啟鉤子函數(shù)
if (this.needBeforeCook()) {
this.beforeCook();
}
this.open();
this.fire();
this.doCook();
this.outfire();
this.close();
}
}
CookMeat.java
/**
* 炒肉
*/
public class CookMeat extends Cook {
@Override
public boolean needBeforeCook() {
return true;
}
@Override
public void beforeCook() {
System.out.println("腌制");
}
@Override
public void doCook() {
System.out.println("紅燒肉");
}
}
CookVegatables.java
public class CookVegatables extends Cook {
@Override
public void doCook() {
System.out.println("炒蔬菜");
}
}
單元測試
import com.netease.learn.designPattern.templateMethod.Cook;
import com.netease.learn.designPattern.templateMethod.CookMeat;
import com.netease.learn.designPattern.templateMethod.CookVegatables;
import org.junit.Test;
public class TemplateMethodTest {
@Test
public void test1() {
Cook cook = new CookMeat();
cook.cook();
}
@Test
public void test2() {
Cook cook = new CookVegatables();
cook.cook();
}
}