設計模式之策略模式

客戶需求

/**
 * 需求:
 * 
 * 四種鴨子,每種鴨子都會顯示自己的名稱且都會游泳,但也有不同之處:
 * 
 * 紅頭鴨(RedHeadDuck):可以用翅膀飛并且會呱呱叫
 * 
 * 綠頭鴨(MallardDuck):可以用腳飛但不會叫
 * 
 * 橡皮鴨(RubberDuck):不會飛但會吱吱叫
 * 
 * 誘餌鴨(DecoyDuck):既不會飛也不會叫
 * 
 * 請用代碼描述以上幾種鴨子
 * 
 * 
 */

程序設計

1、直接利用繼承如何?

將以上四種行為全部寫到Duck這個基類中,然后子類重寫飛和叫的行為。但若以后每加一種鴨子,都有一種不同行為,難道每個子類又都重寫這個方法?很顯然,這種方式不利于以后的擴展和維護

2、直接利用接口如何?

將飛和叫的行為從基類中抽取出來,將其分別放到飛的接口和叫的接口中,讓有需要的鴨子去實現(xiàn)就可以了。貌似可行,可以解決一個鴨子中不會出現(xiàn)本身沒有的行為。但若以后的鴨子越來越多,都實現(xiàn)這兩個接口的話,那么就造成了代碼復用性非常低。

3、找出程序中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起

在Duck類中,每種鴨子都具備的方法是顯示和游泳這兩種行為,而變化的行為是飛和叫,所以,我們把這兩個變化的行為分別放到兩個接口中進行封裝起來

  • Duck類

      public abstract class Duck
      {
          public abstract void display();
          public void swim()
          {
              System.out.println("在河邊游泳");
          }
      }
    
  • 飛的行為

      public interface FlyBehaviour
      {
          void fly();
      }
    
  • 叫的行為

      public interface SoundBehaviour
      {
          void makeSound();
      }
    

4、針對接口編程,而不是針對實現(xiàn)編程(設計原則二)

根據(jù)子類自己的行為,分別實現(xiàn)不同的的行為接口

  • 用翅膀飛

      public class FlyWithWings implements FlyBehaviour
      {
          @Override
          public void fly()
          {
              System.out.println("用翅膀飛");
          }
      }
    
  • 用腳飛

      public class FlyWithFoot implements FlyBehaviour
      {
          @Override
          public void fly()
          {
              System.out.println("用腳飛,蜻蜓點水");
          }
      }
    
  • 不會飛

      public class FlyNoWay implements FlyBehaviour
      {
          @Override
          public void fly()
          {
              System.out.println("飛不起來");
          }
      }
    
  • 呱呱叫

      public class QuackSound implements SoundBehaviour
      {
          @Override
          public void makeSound()
          {
              System.out.println("呱呱叫");
          }
      }
    
  • 吱吱叫

      public class SqueakSound implements SoundBehaviour
      {
          @Override
          public void makeSound()
          {
              System.out.println("吱吱叫");
          }
      }
    
  • 啞鴨

      public class MuteSound implements SoundBehaviour
      {
          @Override
          public void makeSound()
          {
              System.out.println("叫不出聲音,啞鴨");
          }
      }
    

這樣的設計,可以讓飛和叫的動作被其他的對象復用,因為這些行為已經(jīng)與鴨子類無關了。當我們想繼續(xù)新增一些行為時,既不會影響到既有的行為類,也不會影響“使用”到飛和叫行為的鴨子類。

現(xiàn)在鴨子類的的行為飛和叫并未定義在自己的類中,那怎樣與行為類發(fā)生聯(lián)系呢?

  • 在Duck類中添加兩個實例變量,類型為FlyBehaviour和SoundBehaviour,新加兩個方法來代替之前出現(xiàn)在Duck類中的飛和叫的行為

      private FlyBehaviour    mFlyBehaviour;
      private SoundBehaviour  mSoundBehaviour;
    
      public void performFlyBehaviour()
      {
          mFlyBehaviour.fly();
      }
    
      public void performMakeSoundBehaviour()
      {
          mSoundBehaviour.makeSound();
      }
    
  • 在Duck類中對外提供動態(tài)設置行為類型

      public void setFlyBehaviour(FlyBehaviour fb)
      {
          mFlyBehaviour = fb;
      }
    
      public void setMakeSoundBehaviour(SoundBehaviour sb)
      {
          mSoundBehaviour = sb;
      }
    

接下來我們看看子類的實現(xiàn)

    /**
     * 紅頭鴨 可以用翅膀飛并且會呱呱叫
     * 
     * 
     */
    public class RedHeadDuck extends Duck
    {
        @Override
        public void display()
        {
            System.out.println("我是紅頭鴨哦!");
        }
    }
    ------------------------------------------------------
    /**
     * 綠頭鴨 可以用腳飛但不會叫
     * 
     * 
     */
    public class MallardDuck extends Duck
    {
        @Override
        public void display()
        {
            System.out.println("我是綠頭鴨哦!");
        }
    }
    ------------------------------------------------------
    /**
     * 橡皮鴨 會吱吱叫但不會飛
     * 
     * 
     */
    public class RubberDuck extends Duck
    {
        @Override
        public void display()
        {
            System.out.println("我是橡皮鴨哦!");
        }
    }
    ------------------------------------------------------
    /**
     * 誘餌鴨 既不會飛也不會叫
     * 
     * 
     */
    public class DecoyDuck extends Duck
    {
        @Override
        public void display()
        {
            System.out.println("我是誘餌哦!");
        }
    }

測試代碼

    public class DuckTest
    {
        public static void main(String[] args)
        {
            Duck red = new RedHeadDuck();//多態(tài)
            red.display();
            red.setFlyBehaviour(new FlyWithWings());//多態(tài)
            red.performFlyBehaviour();
            red.setMakeSoundBehaviour(new QuackSound());//多態(tài)
            red.performMakeSoundBehaviour();
            red.swim();
    
            Duck mallard = new MallardDuck();
            mallard.display();
            mallard.setFlyBehaviour(new FlyWithFoot());
            mallard.performFlyBehaviour();
            mallard.setMakeSoundBehaviour(new MuteSound());
            mallard.performMakeSoundBehaviour();
    
            Duck rubber = new RubberDuck();
            rubber.display();
            rubber.setFlyBehaviour(new FlyNoWay());
            rubber.performFlyBehaviour();
            rubber.setMakeSoundBehaviour(new SqueakSound());
            rubber.performMakeSoundBehaviour();
    
            Duck decoy = new DecoyDuck();
            decoy.display();
            decoy.setFlyBehaviour(new FlyNoWay());
            decoy.performFlyBehaviour();
            decoy.setMakeSoundBehaviour(new MuteSound());
            decoy.performMakeSoundBehaviour();
        }
    }
QQ截圖20160412171918.png

程序這樣設計后,不管后面添加多少個鴨子,多少個不同的行為,都能夠不影響之前的代碼,非常利于維護和擴展。下面是整個demo的UML圖:

StrategyDesignPatternUML.png

5、多用組合,少用繼承(設計原則三)

每一個鴨子都有一個FlyBehaviour和一個SoundBehaviour,我們可以將鴨子的飛行和呱呱叫委托給它們代為處理。在本例中,我們將Duck類和兩種行為類結(jié)合起來使用,這就是一種組合(composition);這種做法與直接利用“繼承”不同的地方在于:鴨子的行為不是直接繼承他們的父類,而是和適當?shù)男袨閷ο蟆敖M合”而來的。

知識總結(jié)

在本例中,這樣設計程序,我們稱之為:策略模式(Strategy Pattern)

  • 定義

定義算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立于使用算法的客戶

在本例中,這里的算法族是指鴨子的行為,我們可以把它描述為“一族算法”。

  • 三大設計原則

    • 找出程序中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起
    • 針對接口編程,而不是針對實現(xiàn)編程
    • 多用組合,少用繼承

參考資料

Head First 設計模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 思考: 假設有個需求,模擬鴨子游戲:在游戲中會出現(xiàn)各種各樣的鴨子,一邊游泳戲水,一邊呱呱叫。開始我們的設計吧: 這...
    MarksGui閱讀 184評論 0 0
  • 策略模式,是我們接觸到的第一個設計模式,也是較容易理解的一個模式。我們可以給它下一個定義:** 定義了算法族,分別...
    六尺帳篷閱讀 742評論 0 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,981評論 19 139
  • 一直想把常見的設計模式系統(tǒng)地學習一遍,結(jié)果和大多數(shù)人一樣,過了幾天就沒能堅持下去了。我發(fā)現(xiàn)學習這件事情急不得,往往...
    Neulana閱讀 578評論 5 2
  • 模擬鴨子 模擬一個鴨子的游戲SimUDuck,游戲中會出現(xiàn)各種鴨子,一邊游戲戲水,一邊呱呱叫。現(xiàn)采用oo技術(shù),設計...
    yaSecrets閱讀 271評論 0 0