1. 意圖
定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中,Template Method使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟
2. 動機
假設正在開發一款分析公司檔案的資料挖掘程式,用戶需要向程式輸入各種格式(PDF、DOC或CSV)的檔案,程式則會從這些檔案中抽取出有意義的資料,并以統一的格式將其回傳給用戶,一段時間后,你發現這三個類包含了許多相似的代碼,盡管這些類處理不同資料格式的代碼完全不同,但是資料處理和分析的代碼卻完全一樣,怎樣能在保持演算法結構完整的情況下去除重復代碼呢?另外,還有一個與使用這些類的客戶端代碼相關的問題:客戶端代碼中包含許多條件陳述句,以根據不同的處理物件型別選擇合適的處理程序,如果所有處理資料的類都擁有相同的介面或基類, 那么你就可以去除客戶端代碼中的條件陳述句, 轉而使用多型機制來在處理物件上呼叫函式
模板方法模式建議將演算法分解為一系列步驟,然后將這些步驟改寫為方法,最后在 “模板方法” 中依次呼叫這些方法,步驟可以是抽象的,也可以有一些默認的實作,為了能夠使用演算法,客戶端需要自行提供子類并實作所有的抽象步驟,如有必要還需重寫一些步驟(但這一步中不包括模板方法自身),有兩種型別的步驟:
- 抽象步驟必須由各個子類來實作
- 可選步驟已有一些默認實作, 但仍可在需要時進行重寫
還有另一種名為鉤子的步驟,鉤子是內容為空的可選步驟,即使不重寫鉤子,模板方法也能作業,鉤子通常放置在演算法重要步驟的前后,為子類提供額外的演算法擴展點
3. 適用性
- 一次性的實作一個演算法的不變部分,并將可變的行為留給子類來實作
- 各子類中公共的行為應該被提取出來并集中到一個公共父類中以避免代碼重復,
- 控制子類擴展,模板方法只在特定點呼叫鉤子操作,這樣只允許在這些點進行擴展
4. 結構
5. 效果
模板方法是一種代碼復用的基本技術,它們在類別庫中尤為重要,提取了類別庫中的公共行為,模板方法導致了一種反向的控制結構,這種結構有時被稱為好萊塢法則,即一個父類呼叫一個子類的操作,而不是相反
模板方法呼叫下列型別的操作:
1. 具體操作(ConcreteClass或對客戶類的操作)
2. 具體的AbstractClass的操作
3. 原語操作(AbstractClass中定義抽象的原語操作,具體的子類將重定義它們以實作一個演算法的各步驟)
4. 工廠方法
5. 鉤子操作,它提供了預設行為,子類可以在必要時進行擴展,鉤子操作在通常情況下是空操作
6. 代碼實作
本例中,模版方法模式定義了一個可與社交網路協作的演算法,與特定社交網路相匹配的子類將根據社交網路所提供的API來實作這些步驟
networks/Network.java: 基礎社交網路類
package template_method.networks; /** * @author GaoMing * @date 2021/7/25 - 21:47 * Base class of social network. */ public abstract class Network { String userName; String password; Network() {} /** * Publish the data to whatever network. */ public boolean post(String message) { // Authenticate before posting. Every network uses a different // authentication method. if (logIn(this.userName, this.password)) { // Send the post data. boolean result = sendData(message.getBytes()); logOut(); return result; } return false; } abstract boolean logIn(String userName, String password); abstract boolean sendData(byte[] data); abstract void logOut(); }
networks/Facebook.java: 具體社交網路
package template_method.networks; /** * @author GaoMing * @date 2021/7/25 - 21:47 */ public class Facebook extends Network { public Facebook(String userName, String password) { this.userName = userName; this.password = password; } public boolean logIn(String userName, String password) { System.out.println("\nChecking user's parameters"); System.out.println("Name: " + this.userName); System.out.print("Password: "); for (int i = 0; i < this.password.length(); i++) { System.out.print("*"); } simulateNetworkLatency(); System.out.println("\n\nLogIn success on Facebook"); return true; } public boolean sendData(byte[] data) { boolean messagePosted = true; if (messagePosted) { System.out.println("Message: '" + new String(data) + "' was posted on Facebook"); return true; } else { return false; } } public void logOut() { System.out.println("User: '" + userName + "' was logged out from Facebook"); } private void simulateNetworkLatency() { try { int i = 0; System.out.println(); while (i < 10) { System.out.print("."); Thread.sleep(500); i++; } } catch (InterruptedException ex) { ex.printStackTrace(); } } }
networks/Twitter.java: 另一個社交網路
package template_method.networks; /** * @author GaoMing * @date 2021/7/25 - 21:47 */ public class Twitter extends Network{ public Twitter(String userName, String password) { this.userName = userName; this.password = password; } public boolean logIn(String userName, String password) { System.out.println("\nChecking user's parameters"); System.out.println("Name: " + this.userName); System.out.print("Password: "); for (int i = 0; i < this.password.length(); i++) { System.out.print("*"); } simulateNetworkLatency(); System.out.println("\n\nLogIn success on Twitter"); return true; } public boolean sendData(byte[] data) { boolean messagePosted = true; if (messagePosted) { System.out.println("Message: '" + new String(data) + "' was posted on Twitter"); return true; } else { return false; } } public void logOut() { System.out.println("User: '" + userName + "' was logged out from Twitter"); } private void simulateNetworkLatency() { try { int i = 0; System.out.println(); while (i < 10) { System.out.print("."); Thread.sleep(500); i++; } } catch (InterruptedException ex) { ex.printStackTrace(); } } }
Demo.java: 客戶端代碼
package template_method; import template_method.networks.Network; import template_method.networks.Twitter; import template_method.networks.Facebook; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * @author GaoMing * @date 2021/7/25 - 21:47 */ public class Demo { public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); Network network = null; System.out.print("Input user name: "); String userName = reader.readLine(); System.out.print("Input password: "); String password = reader.readLine(); // Enter the message. System.out.print("Input message: "); String message = reader.readLine(); System.out.println("\nChoose social network for posting message.\n" + "1 - Facebook\n" + "2 - Twitter"); int choice = Integer.parseInt(reader.readLine()); // Create proper network object and send the message. if (choice == 1) { network = new Facebook(userName, password); } else if (choice == 2) { network = new Twitter(userName, password); } network.post(message); } }
運行結果
Input user name: Jhonatan Input password: qswe Input message: Hello, World! Choose social network for posting message. 1 - Facebook 2 - Twitter 2 Checking user's parameters Name: Jhonatan Password: **** .......... LogIn success on Twitter Message: 'Hello, World!' was posted on Twitter User: 'Jhonatan' was logged out from Twitter
7. 與其他模式的關系
- 工廠方法模式是模板方法模式的一種特殊形式,同時,工廠方法可以作為一個大型模板方法中的一個步驟
- 模板方法基于繼承機制:它允許你通過擴展子類中的部分內容來改變部分演算法,策略模式基于組合機制:可以通過對相應行為提供不同的策略來改變物件的部分行為,模板方法在類層次上運作,因此它是靜態的,策略在物件層次上運作,因此允許在運行時切換行為
8. 已知應用
使用示例:模版方法模式在Java框架中很常見,開發者通常使用它來向框架用戶提供通過繼承實作的、對標準功能進行擴展的簡單方式
這里是一些核心 Java 程式庫中模版方法的示例:
java.io.InputStream、java.io.OutputStream、java.io.Reader和java.io.Writer的所有非抽象方法
java.util.AbstractList、java.util.AbstractSet和java.util.AbstractMap的所有非抽象方法
javax.servlet.http.HttpServlet,所有默認發送HTTP 405 “方法不允許” 錯誤回應的do-XXX()方法,你可隨時對其進行重寫
識別方法: 模版方法可以通過行為方法來識別, 該方法已有一個在基類中定義的 “默認” 行為
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/308450.html
標籤:設計模式
上一篇:備忘錄模式(學習筆記)
下一篇:狀態模式(學習筆記)
