一、什麼是模板方法模式
所謂模板方法模式,其實很簡單,可以從模板的角度考慮,就是一個對模板的應用,就好比老師出試卷,每個人的試卷都是一樣的,即都是從老師的原版試卷影印來的,這個原版試卷就是一個模板,可每個人寫在試卷上的答案都是不一樣的,這就是模板方法模式,是不是很好理解。它的主要用途在於將不變的行為從子類搬到超類,去除了子類中的重複程式碼。
模板方法模式(TemplateMethod),定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。UML結構圖如下:
其中,AbstractClass實現類一個模板方法,定義了演算法的骨架,具體子類將重定義PrimitiveOperation以實現一個演算法的步驟;而ConcreteClass實現了PrimitiveOperation以完成演算法中與特定子類相關的步驟。
1. 抽象模板類
定義一個模板方法來組合PrimitiveOperation1()和PrimitiveOperation2()兩個方法形成一個演算法,然後讓子類重定義這兩個方法。
1 public abstract class AbstractClass { 2 3 public abstract void PrimitiveOperation1(); 4 public abstract void PrimitiveOperation2(); 5 6 public void TemplateMethod() { 7 PrimitiveOperation1(); 8 PrimitiveOperation2(); 9 } 10 11 }
2. 具體模板類
這裡定義兩個具體模板類,ConcreteClassA及ConcreteClassB來進行測試,繼承抽象模板類,實現具體方法。
1 public class ConcreteClassA extends AbstractClass { 2 3 @Override 4 public void PrimitiveOperation1() { 5 System.out.println("具體方法A方法1實現"); 6 } 7 8 @Override 9 public void PrimitiveOperation2() { 10 System.out.println("具體方法A方法2實現"); 11 } 12 13 }
3. Client客戶端
通過呼叫模板方法來分別得到不同的結果。
1 public class Client { 2 3 public static void main(String[] args) { 4 AbstractClass abstractClass; 5 6 abstractClass = new ConcreteClassA(); 7 abstractClass.TemplateMethod(); 8 9 abstractClass = new ConcreteClassB(); 10 abstractClass.TemplateMethod(); 11 } 12 13 }
執行結果如下:
二、模板方法模式的應用
1. 何時使用
- 有一些通用的方法時
2. 方法
- 將通用演算法抽象出來
3. 優點
- 封裝不變部分,擴充套件可變部分
- 提取公共部分程式碼,便於維護
- 行為由父類控制,子類實現
4. 缺點
- 每一個不同的實現都需要一個子類實現,導致類的個數增加,使得系統更加龐大
5. 使用場景
- 有多個子類共有的方法,且邏輯相同
- 重要的、複雜的方法,可以考慮作為模板方法
- 重構時,模板方法模式是一個經常使用到的模式,把相同的程式碼抽取到父類中,通過鉤子函式約束其行為
6. 應用例項
- 做試卷,大家題目都是一樣的,只是答案不同
- 對於汽車,車從發動到停車的順序是相同的,不同的是引擎聲、鳴笛聲等
- 造房時,地基、走線、水管都一樣,只有在建築後期才有差異
7. 注意事項
- 為防惡意操作,一般模板方法都加上final關鍵字
三、模板方法模式的實現
下面就以上方提到的做試卷為例,具體實現一下以說明如何將模板方法模式應用在實際案例中,UML圖如下:
1. 試卷抽象類
如下在抽象類中定義三個普通方法代表試卷的問題,再定義三個抽象方法程式碼試題答案。
1 public abstract class TestPaper { 2 3 //問題 4 public void question1() { 5 System.out.println("1+1=?"); 6 System.out.println("答案:" + answer1()); 7 } 8 9 public void question2() { 10 System.out.println("2+2=?"); 11 System.out.println("答案:" + answer2()); 12 } 13 14 public void question3() { 15 System.out.println("3+3=?"); 16 System.out.println("答案:" + answer3()); 17 } 18 19 //答案 20 protected abstract String answer1(); 21 22 protected abstract String answer2(); 23 24 protected abstract String answer3(); 25 }
2. 具體試卷
下面定義一個學生甲的試卷,學生乙的試卷與之相比只是返回的答案不同。
1 public class TestPaperA extends TestPaper { 2 3 @Override 4 protected String answer1() { 5 return "2"; 6 } 7 8 @Override 9 protected String answer2() { 10 return "4"; 11 } 12 13 @Override 14 protected String answer3() { 15 return "6"; 16 } 17 18 }
3. Client客戶端
基於同一個模板,學生甲乙分別進行答題,得到不一樣的結果。
1 public class Client { 2 3 public static void main(String[] args) { 4 System.out.println("學生甲的試卷:"); 5 TestPaper studentA = new TestPaperA(); 6 studentA.question1(); 7 studentA.question2(); 8 studentA.question3(); 9 10 //分隔符 11 System.out.println("------------------------------------------------"); 12 13 System.out.println("學生乙的試卷:"); 14 TestPaper studentB = new TestPaperB(); 15 studentB.question1(); 16 studentB.question2(); 17 studentB.question3(); 18 } 19 20 }
執行結果如下:
當我們要完成在某一細節層次一致的一個過程或一系列步驟,但其個別步驟在更詳細的層次上的實現可能不同時,我們通常考慮用模板方法模式來處理。