你每天都在使用模板方法模式,只是你不知道罷了!
1、定義
定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。他僅僅使用了Java的繼承機制,
2、類圖
AbstractClass叫做抽象模板,它的方法分爲兩類:
基本方法:基本方法也叫做基本操作,是由子類實現的方法,並且在模板方法被調用。
模板方法:可以有一個或幾個,一般是一個具體方法,也就是一個框架,實現對基本方法的調度,完成固定的邏輯。爲了防止惡意的操作,一般模板方法都加上final關鍵字,不允許被覆寫。
ConcreteClass1和ConcreteClass2屬於具體模板,實現父類所定義的一個或多個抽象方法,也就是父類定義的基本方法在子類中得以實現。
3、例子
public abstract class AbstractModel {
public void templateMethod() {
doSomething1();
doSomething2();
}
protected abstract void doSomething2();
protected abstract void doSomething1();
}
public class ConcreteClass1 extends AbstractModel {
@Override
protected void doSomething2() {
System.out.println("i am ConcreteClass1 , doSomething2 success");
}
@Override
protected void doSomething1() {
System.out.println("i am ConcreteClass1 , doSomething1 success");
}
}
public class ConcreteClass2 extends AbstractModel {
@Override
protected void doSomething2() {
System.out.println("i am ConcreteClass2 , doSomething2 success");
}
@Override
protected void doSomething1() {
System.out.println("i am ConcreteClass2 , doSomething1 success");
}
}
使用:
//模板方法
AbstractModel concreteClass1 = new ConcreteClass1();
concreteClass1.templateMethod();
AbstractModel concreteClass2 = new ConcreteClass2();
concreteClass2.templateMethod();
輸出:
I/System.out: i am ConcreteClass1 , doSomething1 success
I/System.out: i am ConcreteClass1 , doSomething2 success
I/System.out: i am ConcreteClass2 , doSomething1 success
I/System.out: i am ConcreteClass2 , doSomething2 success
抽象模板中的基本方法儘量設計爲protected類型,符合迪米特法則,不需要暴露 的屬性或方法儘量不要設置爲protected類型。實現類若非必要,儘量不要擴大父類中的訪問權限。
4、模板方法優缺點
優點:封裝不變不分,擴展可變部分;提取公共部分代碼,便於維護;行爲由父類控制,子類實現。
缺點:一般我們會讓抽象類負責聲明最抽象、最一般的屬性和方法,實現類完成具體的事物屬性和方法。但是模板方法模式卻顛倒了,抽象類定義了部分抽象方法,由子類實現,子類執行的結果影響了父類的結果,也就是子類對父類產生了影響,這在複雜項目中會帶來代碼閱讀的難度。
5、使用場景
多個子類有公有的方法,並且邏輯基本相同時。重要、複雜的算法,可以把核心算法設計爲模板方法,周邊的相關細節功能則由各個子類實現。重構時,模板方法模式是一個經常使用的模式,把相同的代碼抽取到父類中,然後通過鉤子函數約束其行爲。
6、擴展
有時候,我們想要根據外部條件控制基本方法的執行,做如下改造:
public abstract class AbstractModel {
public void templateMethod() {
doSomething1();
if (isAllow()) {
doSomething2();
}
}
protected boolean isAllow() {
return true;
}
protected abstract void doSomething2();
protected abstract void doSomething1();
}
public class ConcreteClass1 extends AbstractModel {
private boolean isAllow = false;
@Override
protected void doSomething2() {
System.out.println("i am ConcreteClass1 , doSomething2 success");
}
@Override
protected void doSomething1() {
System.out.println("i am ConcreteClass1 , doSomething1 success");
}
@Override
protected boolean isAllow() {
return this.isAllow;
}
public void setIsAllow(boolean isAllow) {
this.isAllow = isAllow;
}
}
執行:
ConcreteClass1 concreteClass1 = new ConcreteClass1();
concreteClass1.setIsAllow(false);
concreteClass1.templateMethod();
輸出:
I/System.out: i am ConcreteClass1 , doSomething1 success
執行:
ConcreteClass1 concreteClass1 = new ConcreteClass1();
concreteClass1.setIsAllow(true);
concreteClass1.templateMethod();
輸出:
I/System.out: i am ConcreteClass1 , doSomething1 success
I/System.out: i am ConcreteClass1 , doSomething2 success
在我們的抽象類中isAllow的返回值就是影響了模板方法的執行結果,該方法就叫做鉤子方法(Hook Method)。