C++設計模式之策略模式

這兩年,中國好聲音席捲全國,深受觀衆的青睞,收視率也創造了新高。無論你是否身份低微或是高富帥,都可以通過好聲音這個平臺,實現自己的明星夢。緊隨其後,中國好舞蹈等節目鋪天蓋地席捲而來,給選手一個圓夢的機會。如果想實現明星夢,可以參加中國好聲音,也可以參加中國好舞蹈這些節目,還可以參加星光大道,或者像筷子兄弟一樣,拍攝MV。實現明星夢的方式很多,根據自己的喜愛選擇其中的一種方式。在設計模式中,也存在一種模式,實現某個功能有多種方式,每種方式對應一種算法,根據需要選擇其中的一種方式來實現這個功能,稱之爲策略模式。

1、策略模式

    在策略模式中,我們可以定義一些獨立的類來封裝不同的算法,每一個類封裝一種具體的算法,在這裏,每一個封裝算法的類我們都可以稱之爲一種策略(Strategy),爲了保證這些策略在使用時具有一致性,一般會提供一個抽象的策略類來做規則的定義,而每種算法則對應於一個具體策略類。

    策略模式的主要目的是將算法的定義與使用分開,也就是將算法的行爲和環境分開,將算法的定義放在專門的策略類中,每一個策略類封裝了一種實現算法,使用算法的環境類針對抽象策略類進行編程,符合“針對抽象進行編程原則”。在出現新的算法時,只需要增加一個新的實現了抽象策略類的具體策略類即可。策略模式定義如下:

策略模式(Strategy Pattern):定義一系列算法類,將每一個算法封裝起來,並讓它們可以相互替換,策略模式讓算法獨立於使用它的客戶而變化,也稱爲政策模式(Policy)。策略模式是一種對象行爲型模式。

    策略模式結構並不複雜,但我們需要理解其中環境類Context。

    在策略模式結構圖中包含如下幾個角色:

    Context(環境類):環境類是使用算法的角色,在環境類中維持一個對抽象策略類的引用實例,用於定義所採用的策略,實際上環境類將調用策略類中實現算法的方法。環境類針對抽象策略進行編程,無需知道具體是哪一種策略,只要該策略類中實現了具體的算法方法就可以。在同一時刻,環境類只能使用一種策略,而不能使用多種。

    Strategy(抽象策略類):它爲所支持的算法聲明瞭抽象方法,是所有策略類的父類,它可以是抽象類或具體類,也可以是接口。環境類通過抽象策略類中聲明的方法在運行時調用具體策略類中實現的算法。

    ConcreteStrategy(具體策略類):它實現了在抽象策略類中聲明的算法,在運行時,具體策略類將覆蓋在環境類中定義的抽象策略類對象,使用一種具體的算法實現某個業務處理。

    策略模式是一個比較容易理解和使用的設計模式,策略模式是對算法的封裝,它把算法的責任和算法本身分割開,委派給不同的對象管理。策略模式通常把一個系列的算法封裝到一系列具體策略類裏面,作爲抽象策略類的子類。在策略模式中,對環境類和抽象策略類的理解非常重要,環境類是需要使用算法的類。


2、遊戲武器系統的設計與實現

現需要開發一款射擊類遊戲,玩家可以選擇斧頭、手槍、AK47衝鋒槍三種武器,每種武器都有不同的殺傷力等級,如斧頭殺傷力6級,手槍殺傷力8級,AK47殺傷力10級。要求系統具有擴展性,如果有其他的武器,可以直接進行擴展,請使用策略模式進行設計。

    可以設計一個抽象武器類,具體武器如斧頭、手槍、AK47繼承於抽象武器類。

    武器類的實現代碼如下:

  1. #ifndef _WEAPON_H_  
  2. #define _WEAPON_H_  
  3. #include <iostream>  
  4. #include <string>  
  5. using namespace std;  
  6.   
  7. //抽象武器類(抽象策略)  
  8. class Weapon  
  9. {  
  10. public:  
  11.     //虛方法,使用武器打鬥  
  12.     virtual void Fight(string Player) = 0;  
  13. };  
  14.   
  15.   
  16. //手槍(具體策略)  
  17. class Gun : public Weapon  
  18. {  
  19. public:  
  20.     void Fight(string Player, int nKillAbility)  
  21.     {  
  22.         cout << "遊戲玩家:" << Player << "   戰鬥力:8級" << endl;  
  23.     }  
  24. };  
  25.   
  26.   
  27. //Ak47衝鋒槍(具體策略)  
  28. class Ak47 : public Weapon  
  29. {  
  30. public:  
  31.     void Fight(string Player)  
  32.     {  
  33.         cout << "遊戲玩家:" << Player << "   戰鬥力:10級" <<  endl;  
  34.     }  
  35. };  
  36.   
  37.   
  38. //斧頭(具體策略)  
  39. class Axe : public Weapon  
  40. {  
  41. public:  
  42.     void Fight(string Player)  
  43.     {  
  44.         cout << "遊戲玩家:" << Player << "   戰鬥力:6級" <<  endl;  
  45.     }  
  46. };  
  47.   
  48.   
  49. #endif  
    Weapon是抽象策略類,具體武器Gun、Ak47、Axe是具體的策略類。每個具體策略實現抽象策略中打鬥方法。

    現在需要設計一個遊戲玩家類,也就是環境類,遊戲玩家來使用具體的某種武器。遊戲玩家實現代碼如下:

  1. #ifndef _PLAYER_H_  
  2. #define _PLAYER_H_  
  3. #include <iostream>  
  4. #include <string>  
  5. #include "Weapon.h"  
  6.   
  7. using namespace std;  
  8.   
  9. //遊戲玩家(環境類)  
  10. class Player   
  11. {  
  12. private:  
  13.     //玩家名稱  
  14.     string m_strPlayer;  
  15.   
  16.     //玩家使用的武器  
  17.     Weapon * m_pWeapon;  
  18. public:  
  19.     //設置玩家名稱  
  20.     void SetPlayer(string strPlayer)  
  21.     {  
  22.         m_strPlayer = strPlayer;  
  23.     }  
  24.   
  25.     //設置玩家使用的武器  
  26.     void SetWeapon(Weapon * pWeapon)  
  27.     {  
  28.         m_pWeapon = pWeapon;  
  29.     }  
  30.   
  31.     //玩家使用武器打鬥  
  32.     void Fight()  
  33.     {  
  34.         m_pWeapon->Fight(m_strPlayer);  
  35.     }  
  36. };  
  37.   
  38. #endif  
    遊戲玩家Player維持一個武器類對象的引用。當玩家需要使用某種武器的時候,只需要把武器通過SetWeapon設置給玩家,玩家就可以通過使用這種武器進行打鬥操作,實際上調用的仍是武器類的打鬥方法。測試代碼實現如下:

  1. #include <iostream>  
  2. #include "Player.h"  
  3. #include "Weapon.h"  
  4.   
  5. using namespace std;  
  6.   
  7. int main()  
  8. {  
  9.     //創建Ak47武器對象  
  10.     Weapon * pAk47 = new Ak47();  
  11.   
  12.     //創建玩家對象  
  13.     Player * pPlayer = new Player();  
  14.     pPlayer->SetPlayer("挑戰者");  
  15.     pPlayer->SetWeapon(pAk47);  
  16.   
  17.     //打鬥  
  18.     pPlayer->Fight();  
  19.        
  20.         //銷燬操作  
  21.     delete pPlayer;  
  22.     pPlayer = NULL;  
  23.   
  24.     delete pAk47;  
  25.     pAk47 = NULL;  
  26.   
  27.     return 0;  
  28. }  
    編譯並運行,結果如下:

    因爲遊戲玩家(環境類)針對抽象武器策略進行編程。如果需要更換武器策略,無需修改代碼,只需要把具體武器策略設置給遊戲玩家就可以了,可以隨時切換武器。環境類不需要知道具體使用哪種策略,符合"針對抽象編程原則"。如果需要添加新的武器,只需要增加一個新的武器類,作爲抽象武器類的子類,實現抽象類中的打鬥方法。無需修改之前的代碼,符合"開放封閉原則"。


3、策略模式總結  

    策略模式用於算法的自由切換和擴展,它是應用較爲廣泛的設計模式之一。策略模式對應於解決某一問題的一個算法族,允許用戶從該算法族中任選一個算法來解決某一問題,同時可以方便地更換算法或者增加新的算法。只要涉及到算法的封裝、複用和切換都可以考慮使用策略模式。如果需要添加新的策略,只需要增加一個新的策略類,作爲抽象策略類的子類,實現抽象類中的方法,無需修改之前的代碼,符合"開放封閉原則"。環境類針對抽象策略進行編程,如果需要更換,只需要把具體策略設置給環境類就可以了,可以隨時切策略。環境類不需要知道具體使用哪種策略,符合"針對抽象編程原則。由於引進了環境類,客戶端不需要直接操作具體策略,而是由環境類對策略進行操作處理。環境類起承上啓下的作用,屏蔽高層模塊對策略算法的直接訪問,封裝可能存在的變化。環境類和策略類是一種組合關係,通過組合實現對策略類代碼的複用。

1.主要優點

    策略模式的主要優點如下:

    (1) 策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統的基礎上選擇算法或行爲,也可以靈活地增加新的算法或行爲

    (2) 策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族,恰當使用繼承可以把公共的代碼移到抽象策略類中,從而避免重複的代碼。

    (3) 使用策略模式可以避免多重條件選擇語句。多重條件選擇語句不易維護,它把採取哪一種算法或行爲的邏輯與算法或行爲本身的實現邏輯混合在一起,將它們全部硬編碼在一個龐大的多重條件選擇語句中,比直接繼承環境類的辦法還要原始和落後。使用策略模式,if邏輯被封裝到各個策略中。

    (4) 策略模式提供了一種算法的複用機制,由於將算法單獨提取出來封裝在策略類中,因此不同的環境類可以方便地複用這些策略類。

2.主要缺點

    策略模式的主要缺點如下:

    (1) 客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法。換言之,策略模式只適用於客戶端知道所有的算法或行爲的情況。違背"迪米特法則,也就是和直接朋友通信原則",可以通過簡單工廠模式來解決這個問題。

    (2) 策略模式將造成系統產生很多具體策略類,任何細小的變化都將導致系統要增加一個新的具體策略類。

    (3) 無法同時在客戶端使用多個策略類,也就是說,在使用策略模式時,客戶端每次只能使用一個策略類,不支持使用一個策略類完成部分功能後再使用另一個策略類來完成剩餘功能的情況。

3.策略模式具體應用

    (1)電影院打折算法,成年人全票、VIP6折、兒童票與學生票0.5折優惠。

    (2)數據導出:可以導出到數據庫、也可以導出到Xml,或者文本文件中。比如:系統異常信息Log,可以記錄到數據庫也可以記錄到文件中。再如:網頁上的信息可以轉爲Word格式保存,也可以轉爲PDF格式保存。

    (3)網絡購物,可以選擇貨到付款、也可以選擇U盾支付、還可以選擇支付寶支付等支付方式。

    (4)對於加密軟件的開發,可以選擇不同類型的加密算法,肯定不會只有一種加密算法。同樣遍歷二叉樹,存在先序遍歷。中序遍歷、後序遍歷、層序遍歷等四種遍歷算法,根據需要選擇具體的某種遍歷算法。還有數據結構中的各種排序算法,根據具體情況選擇某個排序算法。

    (5)QQ空間背景風格,可以根據需要選擇某種喜歡的風格,用來裝扮空間。

    (6)UI容器管理器,用於管理各種類型控件佈局方式。存在多種不同的佈局方式,根據美觀程度,選擇某種佈局方式。

    (7)生活中的策略: 條條道路通羅馬,36行行行出狀元,描述的就是各種策略;人生面臨着許多選擇,路怎麼走,該往哪裏走。選擇正確,可能一輩子飛黃騰達,選擇錯誤,也許一輩就默默無聞了,這是一種人生選擇策略。

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