模板設計-base
什么是模板?
舉個簡單的例子,以AABB的格式,寫出一個詞語,你可能會想到,明明白白,干干凈凈等,
這個AABB就是一個模板,對模板心中有了一個清晰的概念之后,我們再來看今天我們的內容,模板設計。
實現思路
我們在父類中,定義處理流程的框架,子類中實現具體處理。
要如何實現這樣的思路呢?
在父類中定義多個抽象方法
,然后由一個模板方法
來進行調用,決定如何使用這些模板方法,就形成了處理流程的框架。
子類繼承父類,只需要實現抽象方法。這樣一來模板方法就留在了父類中,各個子類都可以有自己的實現方式。
代碼實現
我們定義AbstractDisplay作為父類里面有open、print、close 3個抽象方法,一個display模板方法。
charDisplay和StringDisplay作為AbstractDisplay的子類,去實現各自的open、print、close方法。
最終我們用子類統一去調用父類已經實現的display模板方法,查看效果。
public abstract class AbstractDisplay {/*** 交給字類實現的抽象方法(1) open*/public abstract void open();/*** 交給字類實現的抽象方法(2)print*/public abstract void print();/*** 交給字類實現的抽象方法(3)close*/public abstract void close();/*** 模板方法,先調用open在調用5次print方法,最后調用close。* 可以看出,父類中只調用抽象方法,但不實現抽象方法,具體實現交給子類。*/public final void display(){open();for (int i = 0; i < 5; i++) {print();}close();}
}
接下來讓我們看看CharDisplay
和StringDisplay
是如何實現的。
public class CharDisplay extends AbstractDisplay{private char character;public CharDisplay(char c){this.character = c;}@Overridepublic void open() {System.out.print("<<");}@Overridepublic void print() {System.out.print(character);}@Overridepublic void close() {System.out.println(">>");}
}
public class StringDisplay extends AbstractDisplay{private String string;private Integer times;public StringDisplay(String string) {this.string = string;times = string.toCharArray().length;}@Overridepublic void open() {System.out.print("+");for (int i = 0; i < times; i++) {System.out.print("-");}System.out.println("+");}@Overridepublic void print() {System.out.println("|"+string+"|");}@Overridepublic void close() {System.out.print("+");for (int i = 0; i < times; i++) {System.out.print("-");}System.out.println("+");}
}
讓我們在Main類中調用試試看吧
public class Main3 {public static void main(String[] args) {//生成一個持有'H'的CharDisplay實例AbstractDisplay d1 = new CharDisplay('H');//生成一個持有'Hello,world.'的StringDisplay類的實例AbstractDisplay d2 = new StringDisplay("Hello,world.");//由于d1、d2都是AbstractDisplay的實例,可以調用繼承的display方法,實際的程序行為取決于CharDisplay類和StringDisplay類的具體實現d1.display();d2.display();}
}
輸出結果
我們創建了一個AbstractDisplay
的模板,最終卻生成了兩種不同的結果!就像AABB的模板最終可以生成明明白白和干干凈凈一樣!
恭喜你!!掌握了模板設計模式的基本使用!接下來讓我們拓展一下思路。
思路拓展
可以使邏輯處理通用化
使用 Template Method模式究竟能帶來什么好處呢?
讓我們先看看Template Method模式的類圖是什么樣的
使用 Template Method模式究竟能帶來什么好處呢?
這里,它的優點是由于在父類的模板方法中編寫了算法,因此無需在每個子類中再重復編寫算法。
例如,我們沒使用 Template Method模式,而是使用文本編輯器的復制和粘貼功能編寫了多個ConcreteClass角色。此時,會出現ConcreteClass1、ConcreteClass2、Concreteclass3 等很多相似的類。編寫完成后立即發現了Bug還好,但如果是過一段時間才發現在Concreteclass1中有 Bug,該怎么辦呢?這時,我們就必須將這個 Bug的修改反映到所有的 ConcreteClass 角色中才行。
關于這一點,如果是使用 Template Method模式進行編程,當我們在模板方法中發現Bug時只需要修改模板方法即可解決問題。
父類與子類之間的協助
在 Template Method模式中,父類和子類是緊密聯系、共同工作的。因此,在子類中實現父類中聲明的抽象方法時,必須要理解這些抽象方法被調用的時機。在看不到父類的源代碼的情況下想要編寫出子類是非常困難的。
模板設計-plus
練習題一
java.io.Inputstream類使用了Template Method模式。請閱讀官方文檔(JDK的API參考資料 ),從中找出需要用java.io.Inputstream 的子類去實現的方法。
練習題二
上面給出的AbstractDisplay
類的display方法如下
public final void display(){....
}
這里使用了final修飾符,請問這是想表達什么意思呢?
練習題三
如果想要讓示例程序中的open、print、close方法可以被具有繼承關系的類和同一程序包中的類調用,但是不能被無關的其他類調用,應當怎么做呢?
練習題四
Java中的接口與抽象類很相似。接口同樣也是抽象方法的集合,但是在TemplateMethod 模式中,我們卻無法使用接口來扮演AbstractClass角色,請問這是為什么呢?
答案
練習題一
查閱文檔后,你就會知道,InputStream是一個抽象類,其中需要子類實現的抽象方法只有一個就是read
方法啦!
練習題二
final
關鍵字修飾方法的時候,表示定義在父類中的模板方法display
無法在子類中進行重寫,這正和我們期望的一致,我們希望模板方法由父類直接實現,即父類直接定義抽象方法的使用框架,不希望子類再去進行重寫,使用模板設計模式時,子類也不應該重寫模板方法。
練習題三
可以將AbstractDisplay類中的open,print,close方法的可見性聲明為protected。這樣就可以讓繼承該類的子類調用這些方法,而其他包中的類無法調用這些方法(不過同一個包中的類依然可以調用這些方法)。
練習題四
這是因為 TemplateMethod模式中的AbstractClass角色必須實現處理的流程。在抽象類中可以實現一部分方法(例如 AbstractDisplay類中的display方法),但是在接口中是無法實現方法的。因此,在TemplateMethod 模式中,無法用接口替代抽象類。
在Java8之前以上結論成立,在Java8之后,引入了defualt關鍵字,增強了接口的功能,使得接口也可以實現方法。
知識關聯性
Factory Method 模式(工廠模式)
是將 Template Method 模式用于生成實例的一個典型例子。
Strategy模式(策略模式)
在 Template Method 模式中,可以使用繼承改變程序的行為。這是因為 Template Method 模式在父類中定義程序行為的框架,在子類中決定具體的處理。
與此相對的是 Strategy模式,它可以使用委托改變程序的行為。與Template Method 模式中改變部分程序行為不同的是,Strategy模式用于替換整個算法。
模板設計模式(Template Method)的講解到此就結束啦,感謝閱讀。💕