還在使用 if else 寫程式碼?試試 “策略模式” 吧!

純潔的微笑發表於2020-05-08



我們使用的app大多都有分享的功能,我們可以選擇分享到不同的地方,比如微博、微信、QQ等等,雖然是同一個內容,但是分享到不同的平臺就會有不同的處理方式,比如要跳轉到不同的app或者直接複製連結等等。如果讓你來實現這個功能,你會如何實現呢?


還在使用 if else 寫程式碼?試試 “策略模式” 吧!
還在使用 if else 寫程式碼?試試 “策略模式” 吧!
如果你對設計模式不熟悉,那麼第一反應就是有if...else或者switch語句來進行條件判斷,根據使用者的不同選擇而使用不同的處理方法。我們用程式碼簡化地處理一下:
public void Share{
public void shareOptions(String option){
       if(option.equals("微博")){
           //function1();
           //...
      }else if(option.equals("微信")){
           //function2();
           //...
      }else if(option.equals("朋友圈")){
           //function3();
           //...
      }else if(option.equals("QQ")){
           //function4();
           //...
      }
       //...
}
如果只是寫一個這麼簡單的功能,那麼這樣的程式碼也未嘗不可,因為這樣的程式碼量不多,後續也不需要怎麼擴充和修改,維護起來也不算麻煩。但是,我們工作中遇到的都是一些比較複雜的專案,要保證專案的可讀性、可維護性和可擴充性,就必須在程式碼上下功夫了。
這裡我們就要提到一個概念,那就是設計模式了。設計模式是指標對軟體開發過程中重複發生的問題的解決辦法。其中以被稱為Gang of Four(GoF)的4人整理出的23種設計模式最為有名。
當然,我不可能在這裡把23種設計模式全部介紹完,就算全部介紹一遍,而且你還能全部記住,也不一定奏效。首先,你沒有必要全部記下來,因為這23種設計模式並非都是經常使用到的設計模式。其次,死記硬背下這些設計模式沒有任何意義,重要的是在自己腦海中理解設計模式是怎樣解決問題的。
今天我們就談談可以最佳化以上問題的設計模式,策略模式(Strategy  Design Pattern)。

策略模式(Strategy  Design Pattern)

策略指的是計策、謀略,而模式一般指人為整理而成的,在某種場景下重複發生的問題的解決辦法。在程式設計中,我們可以把策略看做是“演算法”,而策略模式,按照GoF的定義,就是我們設計一些演算法,把它們封裝起來,讓它們可以相互替換,這樣就可以輕鬆地切換不同的演算法來解決同一個問題。



我們看一下策略模式中有哪些角色。

  • Strategy(策略)Strategy角色負責決定實現策略所必需的介面(API)。在示例程式中,由strategy介面扮演此角色。

  • ConcreteStrategy(具體的策略)ConcreteStrategy角色負責實現Strategy角色的介面(API),即負責實現具體的策略(戰略、方向、方法和演算法)。

  • Context(上下文)負責使用Strategy角色。Context角色儲存了ConcreteStrategy角色的例項,並使用ConcreteStrategy角色去實現需求(總之,還是要呼叫Strategy角色的介面(API))。

再看看策略模式的類圖:
還在使用 if else 寫程式碼?試試 “策略模式” 吧!
介紹到這裡,相信你對策略模式有了初步的認識,那我們就用策略模式來重構前面的程式碼,讓你加深對策略模式的理解。
//定義策略介面
public interface DealStrategy{
   void dealMythod(String option);
}

//定義具體的策略1
public class DealSina implements DealStrategy{
   @override
   public void dealMythod(String option){
       //...
  }
}

//定義具體的策略2
public class DealWeChat implements DealStrategy{
   @override
   public void dealMythod(String option){
       //...
  }
}

//定義上下文,負責使用DealStrategy角色
public static class DealContext{
   private String type;
   private DealStrategy deal;
   public  DealContext(String type,DealStrategy deal){
       this.type = type;
       this.deal = deal;
  }
   public DealStrategy getDeal(){
       return deal;
   }
   public boolean options(String type){
       return this.type.equals(type);
   }
}


public void Share{
   private static List<
DealContext> algs = new ArrayList();
   
//靜態程式碼塊,先載入所有的策略
   
static {
       algs.add(
new DealContext("Sina",new DealSina()));
       algs.add(
new DealContext("WeChat",new DealWeChat()));
  }
public void shareOptions(String type){
       DealStrategy dealStrategy = 
null;
       
for (DealContext deal : algs) {
           
if (deal.options(type)) {
               dealStrategy = deal.getDeal();
               
break;
          }  
      }
       dealStrategy.dealMythod(type);
  }
}
再回憶一下策略模式中的各個角色,程式碼中的DealStrategy介面就是策略,DealSina和DealWeChat是具體的策略,DealContext就是使用策略的上下文。
所以這樣的程式碼已經符合策略模式的程式碼結構了。我們透過策略模式將策略的定義、建立、使用解耦,讓每一部分都不至於太複雜,也去除了if...else這樣的條件判斷語句,程式碼的可維護性和可擴充性都提高了。
我們把可變的部分放到 了Share 類中的靜態程式碼段中。如果有新的需求,要新增一個分享的方式時,只需要定義好具體的策略,然後修改 Share 類中的靜態程式碼段,其他程式碼都不需要修改。
策略模式還是比較常用的一種設計模式,比如java中給我定義好的Comparator 介面就是策略模式的一個實踐。
如果需要對某個類的排序,而該類沒有實現Comparable介面,那麼可以建立一個實現Comparator介面的比較器即可。透過實現Comparator類來新建一個比較器,然後透過該比較器來對類進行排序。
//策略介面
public interface Comparator<T{
   int compare(T o1, T o2);
}

//需要我們來定義具體的策略
public class sorter implements Comparator{
  public int compare(String s1, String s2) {
      return Integer.compare(s1.length(), s2.length());
 }
}
//一般我們會用更簡潔的Lambda表示式來實現

如果你使用的是Java ,想更符合開閉原則,並對反射有一定了解,那還可以透過反射來避免對類的修改。
你可以透過一個配置檔案或者定義一個註解來標註定義的策略類;透過讀取配置檔案或者搜尋被標註的策略類,透過反射動態地載入這些策略類、建立策略物件;當我們新新增一個策略的時候,只需要將這個新新增的策略類新增到配置檔案或者用定義的註解標註即可。
Strategy模式特意將演算法與其他部分分離開來,只是定義了與演算法相關的介面(APl),然後在程式中以委託的方式來使用演算法。
這樣看起來程式好像變複雜了,其實不然。例如,當我們想要透過改善演算法來提高演算法的處理速度時,如果使用了Strategy模式,就不必修改Strategy角色的介面(API)了,僅僅修改ConcreteStrategy角色即可。
而且,使用委託這種弱關聯關係可以很方便地整體替換演算法。例如,使用Strategy模式編寫棋類程式時,可以方便地根據棋手的選擇切換AI電腦的水平。
至此,我們可以小結出策略模式的使用場景:

  • 一個專案中有許多類,它們之間的區別僅在於它們的行為,希望動態地讓一個物件在許多行為中選擇一種行為時;

  • 一個專案需要動態地在幾種演算法中選擇一種時;

  • 一個物件有很多的行為,不想使用多重的條件選擇語句來選擇使用哪個行為時。


策略模式不僅僅可以最佳化if else程式碼,其主要的作用還是解耦策略的定義、建立和使用,控制程式碼的複雜度,讓每個部分都不至於過於複雜、程式碼量過多。除此之外,對於複雜程式碼來說,策略模式還能讓其滿足開閉原則,新增新策略的時候,最小化、集中化程式碼改動,減少引入 bug 的風險。
還在使用 if else 寫程式碼?試試 “策略模式” 吧!


可能有人會問:狀態模式也可以最佳化if else,那麼策略模式和狀態模式又有什麼不同呢?
讓我們來簡單回顧一下狀態模式的類圖:
還在使用 if else 寫程式碼?試試 “策略模式” 吧!
使用策略模式和狀態模式都可以替換被委託物件,而且它們的類之間的關係也很相似。但是兩種模式的目的不同。
在策略模式中,ConcreteStrategy角色是表示演算法的類。在Strategy模式中,可以替換被委託物件的類。當然如果沒有必要,也可以不替換。
而在狀態模式中,ConcreteState角色是表示“狀態”的類。在State模式中,每次狀態變化時,被委託物件的類都必定會被替換。
好了,關於策略模式我們就是介紹到這裡。你在做專案時用過策略模式嗎?是在什麼場景中使用呢?歡迎留言說說。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31499124/viewspace-2690732/,如需轉載,請註明出處,否則將追究法律責任。

相關文章