Strategy(策略)模式

Strategy--定義一系列的算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。

參與者

Strategy
  •  -- 定義所有支持的算法的公共接口。Context使用這個接口來調用某ConcreteStrategy定義的算法。
ConcreteStrategy(具體策略)
  •  -- 以Strategy接口實現英雄模範具體算法。
Context(上下文)
  • --用一個ConcreteStrategy對象來配置。
  • --維護一個對Strategy對象的引用。
  • --可定義一個接口來讓Strategy訪問它的數據。

適用性
  1. 當存在以下情況時使用Strategy模式
  2. 許多相關的類僅僅是行爲有異。“策略”提供了一種用多個行爲中的一個行爲來配置一個類的方法。
  3. 需要使用一個算法的不同變體。
  4. 算法使用了客戶不應該知道的數據。可使用策略模式以避免暴露覆雜的、與算法相關的數據結構。

 

考慮您要設計一個更換各種符號的工具類TextCharChange,您是否會採用這樣的方式:

public void replace() {
   switch(getChangeType()) {
      case RN_TYPE:   replaceRN();
                          break;
      case N_TYPE: replaceN();
                          break;
      case OTHER_TYPE: replaceOTHER():
                          break;
      ...
   }
}


這麼作的缺點是,日後您要增加更換符號的策略時,會有幾個地方需要修改:增加TYPE常數、增加TextCharChange中的 replaceXXX()方法、增加 replace()方法中的switch case判斷。

像這種策略採用的情況,可以將策略加以封裝爲一個物件,而不是將策略寫死在某個類中,如此一來,策略可以獨立於客戶端,隨時增加變化、增加或減少策略,即使是修改每個策略的內容,也不會對客戶端程式造成影響。

來舉個最簡單的例子,首先要知道Windows與Linux的文字檔案換行符號是不同的,Windows是 /r/n ,而Linux是 /n,今天您要設計一個文字編輯器,在適當的時候,您必須要能隨時轉換這兩種符號,如果不採用上面的策略採用流程的話,要如何設計:

  • TextStrategy.java
public abstract class TextStrategy { 
    protected String text;

    public TextStrategy(String text) { 
        this.text = text; 
    }

    public abstract String replace(); 
}  

 

  • LinuxStrategy.java
public class LinuxStrategy extends TextStrategy { 
    public LinuxStrategy(String text) { 
        super(text); 
    }

    public String replace() { 
        preOperation(); 
        System.out.println(
               text = text.replaceAll("@r@n", "@n")); 
        postOperation(); 

        return text; 
    }

    private void preOperation() { 
        System.out.println("LinuxStrategy preOperation"); 
    }

    private void postOperation() { 
        System.out.println("LinuxStrategy postOperation"); 
    } 
}  

 

  • WindowsStrategy.java
public class WindowsStrategy extends TextStrategy { 
    public WindowsStrategy(String text) { 
        super(text); 
    }

    public String replace() { 
        startOperation(); 
        System.out.println(
                     text = text.replaceAll("@n", "@r@n")); 
        endOperation(); 

        return text; 
    }

    private void startOperation() { 
        System.out.println("WindowsStrategy startOperation"); 
    } 

    private void endOperation() { 
        System.out.println("WindowsStrategy endOperation"); 
    } 
} 

 

  • TextCharChange.java
public class TextCharChange { 
    public static void replace(TextStrategy strategy) { 
        strategy.replace(); 
    } 
} 

 

  • Main.java
public class Main { 
    public static void main(String[] args) { 
        String linuxText = 
            "This is a test text!!@n Oh! Line Return!!@n"; 
        String windowsText = 
            "This is a test text!!@r@n Oh! Line Return@r@n"; 

        // load file, suppose it's Linux's text file 
        // take the WindowsStrategy 
        // I want to change it to Windows' text file 
        TextCharChange.replace(
              new WindowsStrategy(linuxText)); 

        // such-and-such operation..... 
        System.out.println(); 

        // load file, suppose it's Windows' text file 
        // take the LinuxStrategy 
        // I want to change it to Linux's text file 
        TextCharChange.replace(
            new LinuxStrategy(windowsText)); 
    } 
} 


爲了明顯的秀出結果,我們使用@n來表示 '/n' , @r 表示 '/r' 符號,Main中的流程是個假設的情況,何時採用何種策略是隨機的。

在Strategy模式中,使用一個公開的介面replace(),讓客戶端請求,而在實作replace()時,可以任意的組合演算策略,程式中的 preOperation()、postOperation()就是用以示意演算的組合概念,Strategy模式封裝了這些演算過程,使它們易於組合、修改、替換,上面這個例子的UML 類別結構圖如下所示:

Strategy


Strategy模式的UML類別結構圖如下:

Strategy

從行爲上來說,State 模式 與Strategy模式是蠻相近的。

State模式:看當前是什麼狀態,就採取什麼動作。

Strategy模式:看需求(情境)是什麼,採用適當的策略。

不過兩者雖相似,應用的場合稍有不同,State模式中有一個重點在於設定狀態變化,就像 Gof 例子中舉的TCP連線;Strategy策略模式則是直接採用適當的策略的感覺,例如Gof中說的,採用適當的演算法來作正文換行。

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