2008-10-05 67 views
12

我已經看過this explanation on Wikipedia,特別是C++示例,並且沒有認識到僅僅定義3個類,創建實例並調用它們之間的區別,以及該示例。我所看到的僅僅是將兩個其他課程放在這個過程中,並且看不到哪裏會有好處。現在我確信我錯過了一些明顯的東西(樹木) - 有人可以用一個明確的現實世界的例子來解釋它嗎?使用戰略模式的好處在哪裏?


我可以從這麼遠的答案做什麼,在我看來是隻是這樣做的更加複雜的方式:

have an abstract class: MoveAlong with a virtual method: DoIt() 
have class Car inherit from MoveAlong, 
    implementing DoIt() { ..start-car-and-drive..} 
have class HorseCart inherit from MoveAlong, 
    implementing DoIt() { ..hit-horse..} 
have class Bicycle inherit from MoveAlong, 
    implementing DoIt() { ..pedal..} 
now I can call any function taking MoveAlong as parm 
passing any of the three classes and call DoIt 
Isn't this what Strategy intents? (just simpler?) 

[編輯更新] 我指的是功能上面被替換爲另一個類,其中MoveAlong將成爲根據需要設置的屬性,基於在這個新類中實現的算法。 (什麼是證明接受的答案類似。)


[編輯更新] 結論

策略模式有它的用途,但我在吻一個強有力的信徒,會傾向於更直接,更不困惑的技術。大多數情況下,因爲我想傳遞易於維護的代碼(並且'我最有可能是必須進行更改的人員!)。

+0

「是不是這是什麼策略的意圖?(只是更簡單?)「< - - 除了你給你的類的引用,讓其他類在需要的時候調用它,而另一個類只知道它有一個MoveAlong。這是在團隊中更有用的其中一種。 – Gerald 2008-10-05 10:52:30

+0

嗨傑拉爾德我認爲你的例子是一個戰略模式的壞榜樣。是的,它是一種戰略,但在你的情況下,有比實施戰略模式更好的子類。我相當肯定火星時鐘會有不同於地球時鐘的行爲,因此它值得繼承時鐘類。 – RWendi 2008-11-27 04:26:53

+0

大聲笑...我剛看到這個評論。我想,如果你期望在火星上有客戶,那麼在你的設計中可能值得擔心火星鍾。但是由於100%的大多數人的客戶將成爲地球上的人類,並且具有世界時間數據表示,所以我認爲忽略這種邊緣情況是相當安全的,除非是視頻遊戲。 – Gerald 2009-06-05 14:44:30

回答

19

要點是算法分離成可以在運行時插入的類。例如,假設您有一個包含時鐘的應用程序。有很多不同的方法可以繪製時鐘,但大部分底層功能都是一樣的。所以,你可以創建一個時鐘顯示接口:

class IClockDisplay 
{ 
    public: 
     virtual void Display(int hour, int minute, int second) = 0; 
}; 

然後你是迷上了一個計時器,並以每秒一次的更新時鐘顯示的時鐘類。所以你會有這樣的東西:

class Clock 
{ 
    protected: 
     IClockDisplay* mDisplay; 
     int mHour; 
     int mMinute; 
     int mSecond; 

    public: 
     Clock(IClockDisplay* display) 
     { 
      mDisplay = display; 
     } 

     void Start(); // initiate the timer 

     void OnTimer() 
     { 
     mDisplay->Display(mHour, mMinute, mSecond); 
     } 

     void ChangeDisplay(IClockDisplay* display) 
     { 
      mDisplay = display; 
     } 
}; 

然後在運行時你用適當的顯示類實例化你的時鐘。即您可以使用ClockDisplayDigital,ClockDisplayAnalog,ClockDisplayMartian全部實現IClockDisplay接口。

因此,您可以通過創建一個新類來添加任何類型的新時鐘顯示,而不必混淆Clock類,也無需重寫可能會混亂維護和調試的方法。

1

在維基百科示例中,可以將這些實例傳遞給一個不必關心這些實例屬於哪個類的函數。該函數只是在傳遞的對象上調用execute,並知道正確的事情將會發生。

戰略模式的一個典型例子是文件如何在Unix中工作。給定一個文件描述符,你可以讀取它,寫入它,輪詢它,尋找它,發送ioctl等,而不必知道你是否正在處理文件,目錄,管道,套接字,設備等(當然,一些操作,如seek,不適用於管道和插座,但在這些情況下,讀寫操作就可以正常工作)。

這意味着您可以編寫通用代碼來處理所有這些不同類型的「文件」,而不必編寫單獨的代碼來處理文件和目錄等。Unix內核負責將調用委託給正確的代碼。

現在,這是在內核代碼中使用的策略模式,但您沒有指定它必須是用戶代碼,只是一個真實世界的例子。 :-)

10

在Java中使用一個密碼輸入流解密,像這樣:

String path = ... ; 
InputStream = new CipherInputStream(new FileInputStream(path), ???); 

但密碼流沒有你打算用什麼加密算法知識或塊大小,填充等策略...新的算法會一直添加,所以對它們進行硬編碼是不實際的。相反,我們傳遞一個密碼策略對象告訴它如何執行解密...

String path = ... ; 
Cipher strategy = ... ; 
InputStream = new CipherInputStream(new FileInputStream(path), strategy); 

一般來說,你使用的策略模式任何時候你有一個知道的任何對象它需要做什麼但不是如何來做到這一點。另一個很好的例子是Swing中的佈局管理器,儘管在這種情況下它並沒有很好地工作,請參閱Totally GridBag以獲得有趣的插圖。

注意:這裏有兩種模式,因爲流中流的包裝是Decorator的示例。

2

這個設計模式允許封裝算法在類中。

使用策略(客戶端類)的類與算法實現分離。您可以更改算法實現,或者添加新算法,而無需修改客戶端。這也可以動態完成:客戶可以選擇他將要使用的算法。

舉一個例子,設想一個需要將圖像保存到文件的應用程序;圖像可以保存爲不同的格式(PNG,JPG ...)。編碼算法將全部在共享相同接口的不同類中實現。客戶端類將根據用戶的喜好選擇一個。

4

策略模式允許您在不擴展主類的情況下利用多態性。本質上,您將所有可變部分放置在策略接口和實現中,並且主類將它們委託給它們。如果你的主對象只使用一種策略,它幾乎與每個子類中的抽象(純虛)方法和不同的實現相同。

的戰略方針提供了一些好處:

  • 可以在運行時改變策略 - 比較這對改變在運行時類的類型,這是對於非虛方法
  • 要困難得多,編譯器特定的和不可能的
  • 一個主要類可以使用多個策略,允許您以多種方式重新組合它們。考慮一個遍歷樹並根據每個節點和當前結果評估函數的類。你可以有一個步行策略(深度優先或寬度優先)和計算策略(一些函子 - 即「計數正數」或「總和」)。如果您不使用策略,則需要爲每個步行/計算組合實施子類。
  • 代碼更容易維護的修改或理解策略並不要求你瞭解整個主要對象

的缺點是,在許多情況下,策略模式是一個矯枉過正 - 開關/箱運營商這是有原因的。考慮從簡單的控制流程語句(開關/案例或if)開始,然後僅在必要時移至類層次結構,並且如果您有多個可變性維度,請從中提取策略。函數指針落在這個連續的中間。

推薦閱讀:

5

戰略與決策/選擇之間存在差異。大多數情況下,我們會在代碼中處理決策/選擇,並使用if()/ switch()構造來實現它們。當需要將邏輯/算法與使用分離時,策略模式非常有用。

舉一個例子,考慮輪詢機制,其中不同的用戶會檢查資源/更新。現在,我們可能希望以更快的週轉時間或更多細節通知一些有用的用戶。 Essentailly所使用的邏輯根據用戶角色而改變。戰略從設計/架構的角度來看是有意義的,在較低的粒度水平上,它應該總是被質疑。

2

查看此問題的一種方法是當您想要執行各種操作並在運行時確定這些操作時。如果創建散列表或策略字典,則可以檢索與命令值或參數相對應的策略。一旦你的子集被選中,你可以簡單地迭代策略列表並連續執行。

一個具體的例子是計算一個訂單的總數。您的參數或命令將是基本價格,地方稅,城市稅,州稅,地面運輸和優惠券折扣。處理訂單變化時靈活性起作用 - 有些州不會有銷售稅,而其他訂單則需要申請優惠券。您可以動態分配計算順序。只要你計算了所有的計算結果,你就可以容納所有的組合而不需要重新編譯。

0

策略模式對簡單的想法起作用,即「贊成構造與繼承」,以便策略/算法可以在運行時更改。舉例說明一下,我們需要根據其類型來加密不同的消息,例如, MAILMESSAGE,ChatMessage等

class CEncryptor 
{ 
    virtual void encrypt() = 0; 
    virtual void decrypt() = 0; 
}; 
class CMessage 
{ 
private: 
    shared_ptr<CEncryptor> m_pcEncryptor; 
public: 
    virtual void send() = 0; 

    virtual void receive() = 0; 

    void setEncryptor(cost shared_ptr<Encryptor>& arg_pcEncryptor) 
    { 
     m_pcEncryptor = arg_pcEncryptor; 
    } 

    void performEncryption() 
    { 
     m_pcEncryptor->encrypt(); 
    } 
}; 

現在在運行時可以實例化不同的消息繼承的CMessage(如CMailMessage:公衆的CMessage)用不同的加密器(如CDESEncryptor:公共CEncryptor)

CMessage *ptr = new CMailMessage(); 
ptr->setEncryptor(new CDESEncrypto()); 
ptr->performEncryption();