文章出處

1. 模板方法的一個實例

        這一節主要來學習一下設計模式中的模板方法模式。我們先來看一個例子:假如現在老板讓你做一個汽車的模型,要求只要完成基本功能即可,不考慮擴展性,那你會怎么做呢?我們首先會根據經驗設計一個類圖:

       由這個類圖可知,非常簡單的實現了悍馬車,該車有兩個型號H1和H2。那現在我們開始實現這兩個型號的悍馬車,首先我們得把抽象類寫好,然后兩個不同的模型實現類通過簡單的繼承就可以實現要求。首先看看抽象類的代碼:

public abstract class HummerModel {  
    public abstract void start(); //發動  
    public abstract void stop();  //停止  
    public abstract void alarm(); //鳴笛  
    public abstract void engineBoom(); //轟鳴  
    public abstract void run(); //車總歸要跑  
}  

簡單到不行,下面我們來實現兩個悍馬的模型:

//悍馬H1  
public class HummerH1 extendsHummerModel {  
  
    @Override  
    public void start() {  
        System.out.println("H1發動……");  
    }  
  
    @Override  
    public void stop() {  
        System.out.println("H1停止……");  
    }  
  
    @Override  
    public void alarm() {  
        System.out.println("H1鳴笛……");  
    }  
  
    @Override  
    public void engineBoom() {  
        System.out.println("H1轟鳴……");  
    }  
  
    @Override  
    public void run() {  
        this.start();  
        this.engineBoom();  
        this.alarm();  
        this.stop();  
    }  
}  
  
//悍馬H2  
public class HummerH2 implements HummerModel {  
  
    @Override  
    public void start() {  
        System.out.println("H2發動……");  
    }  
  
    @Override  
    public void stop() {  
        System.out.println("H2停止……");  
    }  
  
    @Override  
    public void alarm() {  
        System.out.println("H2鳴笛……");  
    }  
  
    @Override  
    public void engineBoom() {  
        System.out.println("H2轟鳴……");  
    }  
  
    @Override  
    public void run() {  
        this.start();  
        this.engineBoom();  
        this.alarm();  
        this.stop();  
    }  
}  

很明顯,已經發現代碼有點問題了,兩個悍馬的run方法完全相同。所以這個run方法應該出現在抽象類中,不應該在實現類中,抽象是所有子類的共性封裝。所以我們修改一下抽象類:

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();  
    }  
}  

  這樣兩個實現類就不用實現run方法了,可以直接拿來用。其實,這就是模板方法模式。

 

2. 模板方法模式的定義

        模板方法模式很簡單,它的定義是:Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses define certain steps of an algorithm without changing the algorithm's structure. 即定義一個操作中的算法框架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構即可沖定義該算法的某些特定步驟。模板方法模式的通用類圖如下:

        模板方法模式確實很簡單,僅僅使用了Java的繼承機制,但是它是一個應用非常廣泛的模式,其中AbstractClass叫做抽象模板,它的方法分為兩類:基本方法(由子類去實現)和模板方法(可以有一個或多個,也就是一個框架,實現對基本方法的調度,完成固定的邏輯)。為了防止惡意的操作,一般模板方法上都添加上final關鍵字,不允許被覆寫。我們來看一下AbstractClass模板:

public abstract class AbstractClass {  
    //基本方法  
    protected abstract void doSomething();  
    protected abstract void doAnything();  
    //模板方法  
    public void templateMethod() {  
        //調用基本方法,完成相關的邏輯  
        this.doAnything();  
        this.doSomething();  
    }  
}  

     具體實現類就不寫了……

 

3. 模板方法模式的優缺點

      優點:

        1)封裝不變部分,擴展可變部分:把認為不變部分的算法封裝到父類實現,可變部分則可以通過繼承來實現,很容易擴展。

        2)提取公共部分代碼,便于維護:上面悍馬的例子就是個很好的解釋。

        3)行為由父類控制,由子類實現。

      缺點:

        模板方法模式顛倒了我們平常的設計習慣:抽象類負責聲明最抽象、最一般的事物屬性和方法,實現類實現具體的事物屬性和方法。在復雜的項目中可能會帶來代碼閱讀的難度。

4. 模板方法模式的擴展

        還是上面那個悍馬的例子,現在老板說這車干嘛跑起來就要鳴笛,太吵了,難道不是應該讓用戶決定它是否要鳴笛么?好像確實是這樣的……那好辦,我們可以修改一下抽象模板類中的方法:

public abstract class HummerModel {  
    protected abstract void start(); //發動  
    protected abstract void stop();  //停止  
    protected abstract void alarm(); //鳴笛  
    protected abstract void engineBoom(); //轟鳴  
    final public void run() { //車總歸要跑  
        this.start();  
        this.engineBoom();  
        if(this.isAlarm()) {//想讓它叫就叫,不想就不叫        
            this.alarm();  
        }  
        this.stop();  
    }  
    protected boolean isAlarm() { //我們加了一個判斷方法,默認返回true  
        return true;  
    }  
}  

 我們在模板類中增加了一個判斷方法來判斷是否要鳴笛,現在就好辦了,具體實現類只要重寫這個方法就可以做到人為控制是否要鳴笛了,下面我們來看一下實現類:

public class HummerH1 extends HummerModel {  
  
    private boolean alarmFlag = true; //判斷標記  
    @Override  
    public void start() {  
        System.out.println("H1發動……");  
    }  
  
    @Override  
    public void stop() {  
        System.out.println("H1停止……");  
    }  
  
    @Override  
    public void alarm() {  
        System.out.println("H1鳴笛……");  
    }  
  
    @Override  
    public void engineBoom() {  
        System.out.println("H1轟鳴……");  
    }  
      
    @Override  
    protected boolean isAlarm() { //覆寫isAlarm方法,返回判斷標記  
        return this.alarmFlag;  
    }  
      
    public void setAlarm(boolean isAlarm) { //設置判斷標記  
        this.alarmFlag = isAlarm;  
    }  
      
}  

 這個實現很好,我們在實現類中定義一個判斷標記,然后對外提供一個public接口setAlarm來讓外界設置這個判斷標記,這就像是開關一樣,想讓它ture和false都行。這個isAlarm方法俗稱鉤子方法。有了鉤子方法的模板方法模式才算完美,大家可以想象一下,由子類的一個方法返回值決定公共部分的執行結果,這個是很有吸引力的。我們來測試一下:

public class Test {
    public static void main(String[] args) throws IOException {
        System.out.println("----H1型焊馬-----");
        System.out.println("是否需要喇叭聲響? 0-不需要 1-需要");
        String type = new BufferedReader(new InputStreamReader(System.in)).readLine();
        /*Scanner scanner = new Scanner(System.in);//創建輸入流掃描器
        String type=scanner.nextLine();*/
         HummerH1 h1 = new HummerH1();
         if(type.equals("0")) {
             h1.setAlarm(false);
         }
         h1.run();
    }
}

  當輸入不同的指令后,就會決定不同的動作:即要不要鳴笛,至此,這個模板方法模式就介紹完了。

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()