Java進階篇設計模式之十一 ---- 策略模式和模板方法模式

前言

上一篇中咱們學習了行爲型模式的訪問者模式(Visitor Pattern)和中介者模式(Mediator Pattern)。本篇則來學習下行爲型模式的兩個模式,策略模式(Strategy Pattern)和模板模式(Template Pattern)。html

策略模式

簡介java

策略模式(Strategy Pattern)屬於對象的行爲模式。其用意是針對一組算法,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換。策略模式使得算法能夠在不影響到客戶端的狀況下發生變化。
其主要目的是經過定義類似的算法,替換if else 語句寫法,而且能夠隨時相互替換。git

策略模式主要由這三個角色組成,環境角色(Context)、抽象策略角色(Strategy)和具體策略角色(ConcreteStrategy)。github

  • 環境角色(Context):持有一個策略類的引用,提供給客戶端使用。
  • 抽象策略角色(Strategy):這是一個抽象角色,一般由一個接口或抽象類實現。此角色給出全部的具體策略類所需的接口。
  • 具體策略角色(ConcreteStrategy):包裝了相關的算法或行爲。

示例圖以下:
在這裏插入圖片描述算法

這裏爲了方便理解,咱們就拿剛學習Java的時候使用計算方法來講吧。
在使用計算器進行計算的時候,會常常用到加減乘除方法。若是咱們想獲得兩個數字相加的和,咱們須要用到「+」符號,獲得相減的差,須要用到「-」符號等等。雖然咱們能夠經過字符串比較使用if/else寫成通用方法,可是計算的符號每次增長,咱們就不得不加在原先的方法中進行增長相應的代碼,若是後續計算方法增長、修改或刪除,那麼會使後續的維護變得困難。
可是在這些方法中,咱們發現其基本方法是固定的,這時咱們就能夠經過策略模式來進行開發,能夠有效避免經過if/else來進行判斷,即便後續增長其餘的計算規則也可靈活進行調整。設計模式

首先定義一個抽象策略角色,並擁有一個計算的方法。ide

interface CalculateStrategy {
   int doOperation(int num1, int num2);
}

而後再定義加減乘除這些具體策略角色並實現方法。學習

那麼代碼以下:測試

class OperationAdd implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
       return num1 + num2;
   }
}

class OperationSub implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
       return num1 - num2;
   }
}

class OperationMul implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
       return num1 * num2;
   }
}

class OperationDiv implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
       return num1 / num2;
   }
}

最後在定義一個環境角色,提供一個計算的接口供客戶端使用。
代碼以下:this

class  CalculatorContext {
    private CalculateStrategy strategy;

    public CalculatorContext(CalculateStrategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

編寫好以後,那麼咱們來進行測試。
測試代碼以下:

public static void main(String[] args) {
           int a=4,b=2;
          CalculatorContext context = new CalculatorContext(new OperationAdd());    
          System.out.println("a + b = "+context.executeStrategy(a, b));
     
          CalculatorContext context2 = new CalculatorContext(new OperationSub());      
          System.out.println("a - b = "+context2.executeStrategy(a, b));
     
          CalculatorContext context3 = new CalculatorContext(new OperationMul());    
          System.out.println("a * b = "+context3.executeStrategy(a, b));
    
          CalculatorContext context4 = new CalculatorContext(new OperationDiv());    
          System.out.println("a / b = "+context4.executeStrategy(a, b));
}

輸出結果:

a + b = 6
            a - b = 2
            a * b = 8
            a / b = 2

策略模式優勢:

擴展性好,能夠在不修改對象結構的狀況下,爲新的算法進行添加新的類進行實現;
靈活性好,能夠對算法進行自由切換;

策略模式缺點:

使用策略類變多,會增長系統的複雜度。;
客戶端必須知道全部的策略類才能進行調用;

使用場景:

若是在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式能夠動態地讓一個對象在許多行爲中選擇一種行爲;
一個系統須要動態地在幾種算法中選擇一種;
若是一個對象有不少的行爲,若是不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現;

模板模式

簡介

模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類能夠按須要重寫方法實現,但調用將以抽象類中定義的方式進行。 這種類型的設計模式屬於行爲型模式。定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。

模板模式,其主要的的思想就是作一個模板,提供給客戶端進行調用。除去生活中咱們常常用到的簡歷模板、合同模板等等,Java中也有很經典的模板使用,那就是Servlet,HttpService類提供了一個service()方法,這個方法調用七個do方法中的一個或幾個,完成對客戶端調用的響應。這些do方法須要由HttpServlet的具體則由子類提供。

模板模式主要由抽象模板(Abstract Template)角色和具體模板(Concrete Template)角色組成。

  • 抽象模板(Abstract Template): 定義了一個或多個抽象操做,以便讓子類實現。這些抽象操做叫作基本操做,它們是一個頂級邏輯的組成步驟;定義並實現了一個模板方法。這個模板方法通常是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操做中,推遲到子類實現。頂級邏輯也有可能調用一些具體方法。

  • 具體模板(Concrete Template): 實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟;每個抽象模板角色均可以有任意多個具體模板角色與之對應,而每個具體模板角色均可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不一樣實現,從而使得頂級邏輯的實現各不相同。

示例圖以下:
https://www.dofactory.com/images/diagrams/net/template.gif

這裏爲了方便理解,咱們依舊使用一個簡單的示例來加以說明。
咱們之前在玩魂鬥羅、雙截龍、熱血物語、忍者神龜等等遊戲的時候,都須要在小霸王遊戲機上插卡,而後啓動遊戲才能玩,其中魂鬥羅這種遊戲,啓動遊戲以後就能夠直接玩了,可是忍者神龜這種遊戲則在啓動遊戲以後,須要選擇其中一個角色才能開始玩。那麼咱們能夠根據這個場景寫出一個通用的模板,主要包含啓動遊戲,玩遊戲,結束遊戲這幾個必須實現的方法,選擇人物這個方法改爲可選。

那麼這個抽象類的代碼以下:

abstract class  Game{
    
    //啓動遊戲
    protected abstract void  runGame();
    //選擇人物
    protected  void choosePerson() {};
    //開始玩遊戲
    protected abstract void startPlayGame();
    //結束遊戲
    protected abstract void endPlayGame();
    
    //模板方法
    public final void play() {
        runGame();
        choosePerson();
        startPlayGame();
        endPlayGame();
    }
    
}

定義好該抽象類以後,咱們再來定義具體模板實現類。這裏定義兩個遊戲類,一個是魂鬥羅,一個忍者神龜。

那麼代碼以下:

class ContraGame extends Game{

   @Override
   protected void runGame() {
       System.out.println("啓動魂鬥羅II...");
   }

   @Override
   protected void startPlayGame() {
       System.out.println("1P正在使用S彈打aircraft...");
   }

   @Override
   protected void endPlayGame() {
       System.out.println("1P被流彈打死了,遊戲結束!");
   }
}

class TMNTGame extends Game{

   @Override
   protected void runGame() {
       System.out.println("啓動忍者神龜III...");
   }

   @Override
   protected void choosePerson() {
       System.out.println("1P選擇了Raph !");
   }

   @Override
   protected void startPlayGame() {
       System.out.println("Raph正在使用絕技 「火箭頭槌」 ");
   }

   @Override
   protected void endPlayGame() {
       System.out.println("Raph 掉進井蓋裏死了,遊戲結束了! ");
   }
}

最後再來進行測試,測試代碼以下:

public static void main(String[] args) {
       Game game = new ContraGame();
       game.play();
       System.out.println();
       game = new TMNTGame();
       game.play();

}

輸出結果:

啓動魂鬥羅II...
1P正在使用S彈打aircraft...
1P被流彈打死了,遊戲結束!

啓動忍者神龜III...
1P選擇了Raph !
Raph正在使用絕技 「火箭頭槌」 
Raph 掉進井蓋裏死了,遊戲結束了!

模板模式優勢:

擴展性好,對不變的代碼進行封裝,對可變的進行擴展;
可維護性好,由於將公共代碼進行了提取,使用的時候直接調用便可;

模板模式缺點:

由於每個不一樣的實現都須要一個子類來實現,致使類的個數增長,會使系統變得複雜;

使用場景:

有多個子類共有邏輯相同的方法;
重要的、複雜的方法,能夠考慮做爲模板方法。

注意事項:

爲防止惡意操做,通常模板方法都加上 final 關鍵詞!

其它

音樂推薦

分享一首很是好聽的輕音樂!
網易雲網友評論:

簡單,重複,毫無華麗旋律,也無厚重悲涼的伴奏。但心恰恰就被牢牢的抓住了。一種茫然卻被迫緊湊的感受。一種不知何所處的心虛。what for?

項目的代碼

java-study是本人在學習Java過程當中記錄的一些代碼,也包括以前博文中使用的代碼。若是感受不錯,但願順手給個start,固然若是有不足,也但願提出。
github地址: https://github.com/xuwujing/java-study

原創不易,若是感受不錯,但願給個推薦!您的支持是我寫做的最大動力! 版權聲明: 做者:虛無境 博客園出處:http://www.cnblogs.com/xuwujing CSDN出處:http://blog.csdn.net/qazwsxpcm  我的博客出處:http://www.panchengming.com

相關文章
相關標籤/搜索