[設計模式]-模板方法模式

前言

在上一篇文章責任鏈模式中提到了模板方法模式,因此這裏簡單介紹一下.

模板方法模式比較簡單,或者說比較常用.在開發過程中,許多人在不知不覺的情況下就會使用,只要他具有良好的面對對象思維.

比如當你寫了DogCat兩個類,發現很多相同的代碼,你自然就會將相同模塊提取抽象成父類,然後將一些公共的方法放到父類中,這樣子就基本實現了模板方式模式.

介紹(摘自《Head FIrst 設計模式》)

在一個方法中定義一個算法的骨架,而將一些詳細的步驟延遲到子類中.

模板方法使得子類可以在不改變算法結果的基礎上,重新定義算法中的某些步驟.

類圖

2019-03-19-23-58-35

角色

抽象模板: 抽象模板一般有一個具體實現的方法,用來定義算法的基礎骨架.還有一些抽象方法留給子類去具體實現.此外還有一些有默認實現的鉤子方法.子類可選實現.

具體模板: 繼承父類的具體方法,實現他們的抽象方法,對於鉤子方法,可以根據自身情況決定是都重寫.

舉個栗子

書上的例子好多了,網絡上也有很多,我自己臨時瞎想一個吧,不保證一定合適.

假如我們現在要實現兩個類,DogCat,並且實現他們的進攻方法.

我們大致實現以下,如下.

Cat:

package design_patterns.template_pattern;

/**
 * created by huyanshi on 2019/3/20
 */
public class Cat1 {

  private void attack() {

    prepared();
    jump();
    bite();

  }

  private void prepared() {

  }

  private void jump() {

  }

  private void bite() {

  }

}

Dog:

package design_patterns.template_pattern;

/**
 * created by huyanshi on 2019/3/20
 */
public class Dog1 {

  private void attack(){

    prepared();
    run();
    shout();
    bite();

  }

  private void prepared(){

  }

  private  void run(){

  }

  private void shout(){

  }

  private void bite(){

  }
}

仔細查看代碼,發現其實他們的攻擊過程很相似.

狗:準備,跑過去,發出聲音,咬住
貓:準備,跳過去,咬住

但是這樣子編碼的話,我們將相同的preparedbite分別寫了兩次,這是不科學的.

很明顯我們可以實現一個父類,將preparedbite在父類裏實現他們分別繼承就好了.

那麼對於shoutjump/run方法呢?就分別實現了嘛?

不是的,jumprun都是移動,只是實現方法不同,也是可以抽象到父類的,那麼shout呢?這就是鉤子的作用了,可以動態控制當前動物是否會發出聲音之後再進行攻擊.

改進後的代碼如下:

首先是動物模板類:

package design_patterns.template_pattern;

/**
 * created by huyanshi on 2019/3/20
 */
public abstract class AnimalTemplate {

  protected boolean isShout;

  public final void attack() {
    //
    prepared();
    move();
    if (isShout) {
      shout();
    }
    bite();

  }

  private void prepared() {
    //具體實現準備方案
    System.out.println("準備");
  }

  abstract void move();

  private void bite() {
    //具體實現咬的方式
    System.out.println("咬");
  }

  abstract void shout();

  public void setShout() {
    isShout = true;
  }
}

注意,代碼中的setShout是鉤子方法,這裏簡單的用一個變量來當做鉤子,此外,preparedbite是具體方法,而moveshout爲抽象方法.

下面是狗的具體實現:

package design_patterns.template_pattern;

/**
 * created by huyanshi on 2019/3/20
 */
public class Dog extends AnimalTemplate {

  @Override
  void move() {
    System.out.println("我是狗,我跑過去");
  }

  @Override
  void shout() {
    System.out.println("我是狗,我叫一下,嚇唬嚇唬他");
  }
}

貓的具體實現:

package design_patterns.template_pattern;

/**
 * created by huyanshi on 2019/3/20
 */
public class Cat extends AnimalTemplate {

  @Override
  void move() {
    System.out.println("我是貓,我跳過去.");
  }

  @Override
  void shout() {
    System.out.println("我是貓,我不叫.");
  }

  @Override
  public void setShout() {
    this.isShout = false;
  }
}

測試類:

package design_patterns.template_pattern;

/**
 * created by huyanshi on 2019/3/20
 */
public class Test {

  public static void main(String[] args) {

    AnimalTemplate dog = new Dog();
    dog.setShout();
    dog.attack();

    AnimalTemplate cat = new Cat();
    cat.setShout();
    cat.attack();
  }
}

輸出結果:

準備
我是狗,我跑過去
我是狗,我叫一下,嚇唬嚇唬他
咬
準備
我是貓,我跳過去.
咬

對比上下的代碼可以發現,在第二個版本中,沒有一些重複的代碼,且子類的邏輯更加清晰,僅僅實現了自己與其他類不相同的部分,或者自己想要實現的部分鉤子方法.

而且當動物越來越多,代碼的總量會越來越少且容易維護,新添加一個動物,只需要繼承動物模板,然後實現moveshout即可.

總結

首先注意一下:在模板方法的attack上,添加了final關鍵字,可以防止該方法被重寫,可以保證attack這一方法,在定義及流程上的正確性及安全性,而具體的實現可以交給子類.

模板方法的優點:
1、封裝不變部分,擴展可變部分。
2、提取公共代碼,便於維護。
3、行爲由父類控制,子類實現。

缺點:

類的個數較多.

完。





ChangeLog

2019-03-20 完成

以上皆爲個人所思所得,如有錯誤歡迎評論區指正。

歡迎轉載,煩請署名並保留原文鏈接。

聯繫郵箱:[email protected]

更多學習筆記見個人博客------>呼延十

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章