設計模式-Strategy Pattern

1、 策略(Strategy)模式

策略模式的用意是針對一組算法,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換。策略模式使得算法能夠在不影響到客戶端的狀況下發生變化。算法

假 設如今要設計一個販賣各種書籍的電子商務網站的購物車(Shopping Cat)系統。一個最簡單的狀況就是把全部貨品的單價乘上數量,可是實際狀況確定比這要複雜。好比,本網站可能對全部的教材類圖書實行每本一元的折扣;對 連環畫類圖書提供每本7%的促銷折扣,而對非教材類的計算機圖書有3%的折扣;對其他的圖書沒有折扣。因爲有這樣複雜的折扣算法,使得價格計算問題須要系 統地解決。設計模式

使用策略模式能夠把行爲和環境分割開來。環境類負責維持和查詢行爲類,各類算法則在具體策略類 (ConcreteStrategy)中提供。因爲算法和環境獨立開來,算法的增減、修改都不會影響環境和客戶端。當出現新的促銷折扣或現有的折扣政策出 現變化時,只須要實現新的策略類,並在客戶端登記便可。策略模式至關於"可插入式(Pluggable)的算法"。ide

2、 策略模式的結構

策略模式是對算法的包裝,是把使用算法的責任和算法自己分割開,委派給不一樣的對象管理。策略模式一般把一個系列的算法包裝到一系列的策略類裏面,做爲一個抽象策略類的子類。用一句話來講,就是:"準備一組算法,並將每個算法封裝起來,使得它們能夠互換。"oop

策略又稱作政策(Policy)模式【GOF95】。下面是一個示意性的策略模式結構圖:學習

 

這個模式涉及到三個角色:網站

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


3、 示意性源代碼

//  Strategy pattern -- Structural example  
using  System;

//  "Strategy"
abstract   class  Strategy
{
  
// Methods
  abstract public void AlgorithmInterface();
}


//  "ConcreteStrategyA"
class  ConcreteStrategyA : Strategy
{
  
// Methods
  override public void AlgorithmInterface()
  
{
    Console.WriteLine(
"Called ConcreteStrategyA.AlgorithmInterface()");
  }

}


//  "ConcreteStrategyB"
class  ConcreteStrategyB : Strategy
{
  
// Methods
  override public void AlgorithmInterface()
  
{
    Console.WriteLine(
"Called ConcreteStrategyB.AlgorithmInterface()");
  }

}


//  "ConcreteStrategyC"
class  ConcreteStrategyC : Strategy
{
  
// Methods
  override public void AlgorithmInterface()
  
{
    Console.WriteLine(
"Called ConcreteStrategyC.AlgorithmInterface()");
  }

}


//  "Context"
class  Context
{
  
// Fields
  Strategy strategy;

  
// Constructors
  public Context( Strategy strategy )
  
{
    
this.strategy = strategy;
  }


  
// Methods
  public void ContextInterface()
  
{
    strategy.AlgorithmInterface();
  }

}


/// <summary>
/// Client test
/// </summary>

public   class  Client
{
  
public static void Main( string[] args )
  
{
    
// Three contexts following different strategies
    Context c = new Context( new ConcreteStrategyA() );
    c.ContextInterface();

    Context d 
= new Context( new ConcreteStrategyB() );
    d.ContextInterface();

    Context e 
= new Context( new ConcreteStrategyC() );
    e.ContextInterface();
  }

}


4、 什麼時候使用何種具體策略角色

在學習策略模式時,學員常問的一個問題是:爲何不能從策略模式中看出哪個具體策略適用於哪種狀況呢?ui

答案很是簡單,策略模式並不負責作這個決定。換言之,應當由客戶端本身決定在什麼狀況下使用什麼具體策略角色。策略模式僅僅封裝算法,提供新算法插入到已有系統中,以及老算法從系統中"退休"的方便,策略模式並不決定在什麼時候使用何種算法。this


5、 一個實際應用策略模式的例子

下面的例子利用策略模式在排序對象中封裝了不一樣的排序算法,這樣以便容許客戶端動態的替換排序策略(包括Quicksort、Shellsort和Mergesort)。spa

//  Strategy pattern -- Real World example  
using  System;
using  System.Collections;

//  "Strategy"
abstract   class  SortStrategy
{
  
// Methods
  abstract public void Sort( ArrayList list );
}


//  "ConcreteStrategy"
class  QuickSort : SortStrategy
{
  
// Methods
  public override void Sort(ArrayList list )
  
{
    list.Sort(); 
// Default is Quicksort
    Console.WriteLine("QuickSorted list ");
  }

}


//  "ConcreteStrategy"
class  ShellSort : SortStrategy
{
  
// Methods
  public override void Sort(ArrayList list )
  
{
    
//list.ShellSort();
    Console.WriteLine("ShellSorted list ");
  }

}


//  "ConcreteStrategy"
class  MergeSort : SortStrategy
{
  
// Methods
  public override void Sort( ArrayList list )
  
{
    
//list.MergeSort();
    Console.WriteLine("MergeSorted list ");
  }

}


//  "Context"
class  SortedList
{
  
// Fields
  private ArrayList list = new ArrayList();
  
private SortStrategy sortstrategy;

  
// Constructors
  public void SetSortStrategy( SortStrategy sortstrategy )
  
{
    
this.sortstrategy = sortstrategy;
  }


  
// Methods
  public void Sort()
  
{
    sortstrategy.Sort( list );
  }


  
public void Add( string name )
  
{
    list.Add( name );
  }


  
public void Display()
  
{
    
foreachstring name in list )
      Console.WriteLine( 
" " + name );
  }

}


/// <summary>
/// StrategyApp test
/// </summary>

public   class  StrategyApp
{
  
public static void Main( string[] args )
  
{
    
// Two contexts following different strategies
    SortedList studentRecords = new SortedList( );
    studentRecords.Add( 
"Samual" );
    studentRecords.Add( 
"Jimmy" );
    studentRecords.Add( 
"Sandra" );
    studentRecords.Add( 
"Anna" );
    studentRecords.Add( 
"Vivek" );

    studentRecords.SetSortStrategy( 
new QuickSort() );
    studentRecords.Sort();
    studentRecords.Display();
  }

}


6、 在什麼狀況下應當使用策略模式

在下面的狀況下應當考慮使用策略模式:設計

1. 若是在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式能夠動態地讓一個對象在許多行爲中選擇一種行爲。

2. 一 個系統須要動態地在幾種算法中選擇一種。那麼這些算法能夠包裝到一個個的具體算法類裏面,而這些具體算法類都是一個抽象算法類的子類。換言之,這些具體算 法類均有統一的接口,因爲多態性原則,客戶端能夠選擇使用任何一個具體算法類,並只持有一個數據類型是抽象算法類的對象。

3. 一個系統的算法使用的數據不可讓客戶端知道。策略模式能夠避免讓客戶端涉及到沒必要要接觸到的複雜的和只與算法有關的數據。

4. 若是一個對象有不少的行爲,若是不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。此時,使用策略模式,把這些行爲轉移到相應的具體策略類裏面,就能夠避免使用難以維護的多重條件選擇語句,並體現面向對象設計的概念。


7、 策略模式的優勢和缺點

策略模式有不少優勢和缺點。它的優勢有:

1. 策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族。恰當使用繼承能夠把公共的代碼移到父類裏面,從而避免重複的代碼。

2. 策 略模式提供了能夠替換繼承關係的辦法。繼承能夠處理多種算法或行爲。若是不是用策略模式,那麼使用算法或行爲的環境類就可能會有一些子類,每個子類提供 一個不一樣的算法或行爲。可是,這樣一來算法或行爲的使用者就和算法或行爲自己混在一塊兒。決定使用哪種算法或採起哪種行爲的邏輯就和算法或行爲的邏輯混 合在一塊兒,從而不可能再獨立演化。繼承使得動態改變算法或行爲變得不可能。

3. 使用策略模式能夠避免使用多重條件轉移語句。多重轉移語句不易維護,它把採起哪種算法或採起哪種行爲的邏輯與算法或行爲的邏輯混合在一塊兒,通通列在一個多重轉移語句裏面,比使用繼承的辦法還要原始和落後。

策略模式的缺點有:

1. 客戶端必須知道全部的策略類,並自行決定使用哪個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法類。換言之,策略模式只適用於客戶端知道全部的算法或行爲的狀況。

2. 策略模式形成不少的策略類。有時候能夠經過把依賴於環境的狀態保存到客戶端裏面,而將策略類設計成可共享的,這樣策略類實例能夠被不一樣客戶端使用。換言之,可使用享元模式來減小對象的數量。


8、 其它

策 略模式與不少其它的模式都有着普遍的聯繫。Strategy很容易和Bridge模式相混淆。雖然它們結構很類似,但它們倒是爲解決不一樣的問題而設計的。 Strategy模式注重於算法的封裝,而Bridge模式注重於分離抽象和實現,爲一個抽象體系提供不一樣的實現。Bridge模式與Strategy模 式都很好的體現了"Favor composite over inheritance"的觀點。

推薦你們讀一讀《IoC 容器和Dependency Injection 模式》,做者Martin Fowler。網上能夠找到中文版的PDF文件。爲策略模式的實施提供了一個很是好的方案。

參考文獻: 閻宏,《Java與模式》,電子工業出版社 [美]James W. Cooper,《C#設計模式》,電子工業出版社 [美]Alan Shalloway  James R. Trott,《Design Patterns Explained》,中國電力出版社 [美]Robert C. Martin,《敏捷軟件開發-原則、模式與實踐》,清華大學出版社 [美]Don Box, Chris Sells,《.NET本質論 第1卷:公共語言運行庫》,中國電力出版社

相關文章
相關標籤/搜索