模板方法模式(Template Method Pattern)。__java

來源:互聯網
上載者:User

定義:

定義一個操作中的演算法的架構,而將一些步驟延遲到子類中。使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。

注意:為了防止惡意的操作,一般模板方法都加上final關鍵字,不允許被覆寫。


通用代碼:

抽象模板類

它的方法分為兩類:

基本方法 基本方法也叫作基本操作,是由子類實現的方法,並且在模板方法被調用。 模板方法 可以有一個或幾個,一般是一個具體方法,也就是一個架構,實現對基本方法的調度,完成固定的邏輯。 public abstract class AbstractClass { // 基本方法 protected abstract void doSomething(); // 基本方法 protected abstract void doAnything(); // 模板方法 public void templateMethod(){ // 調用基本方法,完成相關的邏輯 this.doAnything(); this.doSomething(); } } 具體模板類 public class ContreteClass1 extends AbstractClass { // 實現基本方法 protected void doAnything(){ // 商務邏輯處理 } protected void doSomething(){ // 商務邏輯處理 } } 情境類 public class Client { public static void main(String[] args) { AbstractClass class1 = new ConcreteClass1(); AbstrcatClass class2 = new ConcreteClass2(); // 調用模板方法 class1.templateMethod(); class2.templateMethod(); } } 注意:抽象模板中的基本方法盡量設計為protected類型,符合迪米特法則,不需要暴露或方法盡量不要設定為protected類型。實作類別若非必要,盡量不要擴大父類中的存取權限。
優點: 封裝不變部分,擴充可變部分 把認為是不變部分的演算法封裝到父類實現,而可變部分的則可以通過繼承來繼續擴充。 提取公用部分代碼,便於維護 行為由父類控制,子類實現 基本方法是由子類實現的,因此子類可以通過擴充的方式增加相應的功能,符合開閉原則。
缺點: 按照我們的設計習慣,抽象類別負責聲明最抽象,最一般的事物屬性和方法,實作類別完成具體的事物屬性和方法。但是模板方法模式卻顛倒了,抽象類別定義了部分抽象方法, 由子類實現,子類執行的結果影響了父類的結果,也就是子類對父類產生了影響,這在複雜的項目中,會帶來代碼閱讀的難度,而且也會讓新手產生不適感。
使用情境: 多個子類有共有的方法,並且邏輯基本相同時。 重複、複雜的演算法,可以把核心演算法設計為模板方法,周邊的相關細節功能則由各個子類實現。 重構時,模板方法模式時一個經常使用的模式,把相同的代碼抽取到父類中,然後通過鉤子函數(見“模板方法模式的擴充”)約束其行為。
案例代碼: 抽象悍馬模型 public abstract class HummerModel { // 首先,這個模型要能夠被發動起來,別管是手搖發動,還是電力發動,反正是要能夠發動起來,那這個實現要在實作類別裡了 public abstract void start(); // 能發動,還要能停下來,那才是真本事 public abstract void stop(); // 喇叭會出聲音,是滴滴叫,還是嗶嗶叫 public abstract void alarm(); // 引擎會轟隆隆地響,不響那是假的 public abstract void engineBoom(); // 那模型應該會跑吧,別管是人推,還是電力驅動的,總之要會跑 public void run() { //先發動車 this.start(); // 引擎開始轟鳴 this.engineBoom(); // 然後就開始跑了,跑得過程中遇到一條狗擋道,就按喇叭 this.alarm(); // 到達目的地就停車 this.stop(); } } H1型號悍馬模型(H2型悍馬模型代碼與其一致,省略) public class HummerHiModel extends HummerModel { // H1型號的悍馬車鳴笛 public void alarm() { System.out.println("悍馬H1鳴笛..."); } // 引擎轟鳴聲 public void engineBoom() { System.out.println("悍馬H1引擎聲音是這樣的..."); } // 汽車發動 public void start() { System.out.println("悍馬H1發動..."); } // 停車 public void stop() { System.out.println("悍馬H1停車..."); } } 注意:在軟體開發過程中,如果相同的一段代碼複製過兩次,就需要對設計產生懷疑,架構師要明確地說明為什麼相同的邏輯要出現兩次或更多次。 情境類 public class Client { public static void main(String[] args) { // XX公司要H1型號的悍馬 HummerModel h1 = new HummerH1Model(); // H1模型示範 h1.run(); } }

模板方式的擴充: 環境類比: H1型號的悍馬喇叭想讓它響就響,H2型號的喇叭不要有聲音。 擴充後的抽象模板類 public abstract class HummerModel { // 首先,這個模型要能夠被發動起來,別管是手搖發動,還是電力發動,反正是要能夠發動起來,那這個實現要在實作類別裡了 public abstract void start(); // 能發動,還要能停下來,那才是真本事 public abstract void stop(); // 喇叭會出聲音,是滴滴叫,還是嗶嗶叫 public abstract void alarm(); // 引擎會轟隆隆地響,不響那是假的 public abstract void engineBoom(); // 那模型應該會跑吧,別管是人推,還是電力驅動的,總之要會跑 public void run() { //先發動車 this.start(); // 引擎開始轟鳴 this.engineBoom(); // 然後就開始跑了,跑得過程中遇到一條狗擋道,就按喇叭 if(this.isAlarm()) { this.alarm();
} // 到達目的地就停車 this.stop(); } // 鉤子方法,預設喇叭是會想的 protected boolean isAlarm() { return true; } }
isAlarm()是一個實現方法。其作用是模板方法根據其傳回值決定是否要響喇叭,子類可以覆寫該方法值。 擴充後的H1悍馬 public class HummerHiModel extends HummerModel { private boolean alarmFlag = true; // 要響喇叭 // H1型號的悍馬車鳴笛 public void alarm() { System.out.println("悍馬H1鳴笛..."); } // 引擎轟鳴聲 public void engineBoom() { System.out.println("悍馬H1引擎聲音是這樣的..."); } // 汽車發動 public void start() { System.out.println("悍馬H1發動..."); } // 停車 public void stop() { System.out.println("悍馬H1停車..."); } protected boolean isAlarm() { reuturn this.alarmFlag; } // 要不要響喇叭,是由客戶來決定的 public void setAlarm(boolean isAlarm) { this.alarmFlag = isAlarm; } } 擴充後的H2悍馬 public class Hummer2HiModel extends HummerModel { private boolean alarmFlag = true; // 要響喇叭 // H1型號的悍馬車鳴笛 public void alarm() { System.out.println("悍馬H1鳴笛..."); } // 引擎轟鳴聲 public void engineBoom() { System.out.println("悍馬H1引擎聲音是這樣的..."); } // 汽車發動 public void start() { System.out.println("悍馬H1發動..."); } // 停車 public void stop() { System.out.println("悍馬H1停車..."); } // 預設沒有喇叭的 protected boolean isAlarm() { reuturn false; } } 在我們的抽象類別中isAlarm的傳回值就是影響了模板方法的執行結果,該方法就叫做鉤子方法(Hook Method)。 模板方法模式就是在模板方法中按照一定的規則和順序調用基本方法,具體到前面那個例子,就是run()方法按照規定的順序調用本類的其他方法,並且由isAlarm()方法的傳回值確定run()中的執行順序變更。
最佳實務: 父類是否可以調用子類的方法呢。回答是能,但強烈的、極度的不建議這麼做,那該怎麼做呢。 把子類傳遞到父類的有參構造中,然後調用。 使用反射的方式調用,你使用了反射還有誰不能調用的。 父類調用子類的靜態方法。 父類建立架構,子類在重寫了父類部分的方法後,再調用從父類繼承的方法,產生不同的結果(而這正是模板方法模式)。這是不是也可以理解為父類調用了子類的方法呢。你修改了子類,影響了父類行為的結果,曲線救國的方式實現了父類依賴子類的情境,模板方法模式就是這種效果。 在《xxx In Action》中就說明了,如果你需要擴充功能,可以繼承這個抽象類別,然後覆寫protected方法,再然後就是調用一個類似execute方法,就完成你的擴充開發,非常容易擴充的一種模式。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.