c/c++設計模式---策略模式

白伟碧一些小心得發表於2024-06-11

一個具體範例的逐步重構

Fighter.h

#ifndef __RIGHTER__
#define __RIGHTER__

////增加補充生命值道具(藥品)
//enum ItemAddlife
//{
//    LF_BXD,  //補血丹
//    LF_DHD,  //大還丹
//    LF_SHD,  //守護丹
//};

class ItemStrategy;  //類前向宣告

//戰鬥者父類
class Fighter
{
public:
    Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}
    virtual ~Fighter() {}

public:
    /*
    void UseItem(ItemAddlife djtype) //吃藥補充生命值
    {
        if (djtype == LF_BXD) //道具型別:補血丹
        {
            m_life += 200;//補充200點生命值
            //if (主角中毒)
            //{
            //    停止中毒狀態,也就是主角吃藥後不再中毒
            //}
            //if (主角處於狂暴狀態)
            //{
            //    m_life += 400; //額外再補充400點生命值
            //    m_magic += 200; //魔法值也再補充200點
            //}
        }
        else if (djtype == LF_DHD) //道具型別:大還丹
        {
            m_life += 300;//補充300點生命值
        }
        else if (djtype == LF_SHD) //道具型別:守護丹
        {
            m_life += 500;//補充500點生命值
        }
        //.......其他的一些判斷邏輯,略。。。。。。
    }
    */

public:
    void SetItemStrategy(ItemStrategy* strategy); //設定道具使用的策略
    void UseItem(); //使用道具
    int GetLife(); //獲取人物生命值
    void SetLife(int life);  //設定人物生命值    

protected:
    int m_life;
    int m_magic;
    int m_attack;

    ItemStrategy* itemstrategy = nullptr; //C++11中支援這樣初始化
};

//“戰士”類,父類為Fighter
class F_Warrior :public Fighter
{
public:
    F_Warrior(int life, int magic, int attack) :Fighter(life, magic, attack) {}
};

//“法師”類,父類為Fighter
class F_Mage :public Fighter
{
public:
    F_Mage(int life, int magic, int attack) :Fighter(life, magic, attack) {}
};

#endif

Fighter.cpp

#include <iostream>
#include "Fighter.h"
#include "ItemStrategy.h"

using namespace std;

//設定道具使用的策略
void Fighter::SetItemStrategy(ItemStrategy* strategy)
{
    itemstrategy = strategy;
}

//使用道具(吃藥)
void Fighter::UseItem()
{
    itemstrategy->UseItem(this);
}

//獲取人物生命值
int Fighter::GetLife()
{
    return m_life;
}

//設定人物生命值
void Fighter::SetLife(int life)
{
    m_life = life;
}

ItemStrategy.h

#ifndef _ITEMSTRATEGY__
#define _ITEMSTRATEGY__

//道具策略類的父類
class ItemStrategy
{
public:
    virtual void UseItem(Fighter* mainobj) = 0;
     
};

//補血丹策略類
class ItemStrategy_BXD :public ItemStrategy
{
public:
    virtual void UseItem(Fighter* mainobj)
    {
        mainobj->SetLife(mainobj->GetLife() + 200);  //補充200點生命值
    }
};

//大還丹策略類
class ItemStrategy_DHD :public ItemStrategy
{
public:
    virtual void UseItem(Fighter* mainobj)
    {
        mainobj->SetLife(mainobj->GetLife() + 300);  //補充300點生命值
#include <iostream>
#include "Fighter.h"
#include "ItemStrategy.h"

#ifdef _DEBUG   //只在Debug(除錯)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定義new運算子
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{    
    
}

int main()
{
/*
    Fighter* prole_war = new F_Warrior(1000, 0, 200); //這裡沒有采用工廠模式,如果主角很多,可以考慮採用工廠模式建立物件
    prole_war->UseItem(LF_DHD);

    delete prole_war;     
    */

    //建立主角
    Fighter* prole_war = new F_Warrior(1000, 0, 200);

    //吃一顆大還丹
    ItemStrategy* strategy = new ItemStrategy_DHD(); //建立大還丹策略
    prole_war->SetItemStrategy(strategy); //主角設定大還丹策略,準備吃大還丹
    prole_war->UseItem(); //主角吃大還丹

    //再吃一顆補血丹
    ItemStrategy* strategy2 = new ItemStrategy_BXD(); //建立補血丹策略
    prole_war->SetItemStrategy(strategy2);//主角設定補血丹策略,準備吃補血丹
    prole_war->UseItem(); //主角吃補血丹

    delete strategy;
    delete strategy2;
    delete prole_war;
    
    return 0;
}
}

    }
};


//守護丹策略類
class ItemStrategy_SHD :public ItemStrategy
{
public:
    virtual void UseItem(Fighter* mainobj)
    {
        mainobj->SetLife(mainobj->GetLife() + 500);  //補充500點生命值
    }
};

#endif

myproject.cpp

#include <iostream>
#include "Fighter.h"
#include "ItemStrategy.h"

#ifdef _DEBUG   //只在Debug(除錯)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定義new運算子
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{    
    
}

int main()
{

/*
    Fighter* prole_war = new F_Warrior(1000, 0, 200); //這裡沒有采用工廠模式,如果主角很多,可以考慮採用工廠模式建立物件
    prole_war->UseItem(LF_DHD);

    delete prole_war;     
    */

    //建立主角
    Fighter* prole_war = new F_Warrior(1000, 0, 200);

    //吃一顆大還丹
    ItemStrategy* strategy = new ItemStrategy_DHD(); //建立大還丹策略
    prole_war->SetItemStrategy(strategy); //主角設定大還丹策略,準備吃大還丹
    prole_war->UseItem(); //主角吃大還丹

    //再吃一顆補血丹
    ItemStrategy* strategy2 = new ItemStrategy_BXD(); //建立補血丹策略
    prole_war->SetItemStrategy(strategy2);//主角設定補血丹策略,準備吃補血丹
    prole_war->UseItem(); //主角吃補血丹

    delete strategy;
    delete strategy2;
    delete prole_war;
    
    return 0;


}
策略(Strategy)模式
//(1)一個具體實現範例的逐步重構
//補血道具(藥品):
//a:補血丹:補充200點生命值
//b:大還丹:補充300點生命值
//c:守護丹:補充500點生命值
//Fighter,F_Warrior,F_Mage
//策略 設計模式的定義:定義一系列演算法(策略類),將每個演算法封裝起來,讓它們可以相互替換。換句話說,策略模式通常把一系列演算法
// 封裝到一系列具體策略類中來作為抽象策略類的子類,然後根據實際需要使用這些子類。
//策略類中的三種角色
//a)Context(環境類):該類中維持著一個對抽象策略類的指標或引用。這裡指Fighter類。
//b)Stategy(抽象策略類):定義所支援的演算法的公共介面,是所有策略類的父類。這裡指ItemStrategy類。
//c)ConcreteStrategy(具體策略類):抽象策略類的子類,實現抽象策略類中宣告的介面。這裡指ItemStrategy_BXD、ItemStrategy_DHD、ItemStrategy_SHD。
//策略類的優點:
//a)以擴充套件的方式支援對未來的變化,符合開閉原則。
//遇到大量不穩定的if條件分支 或者switch分支,就要優先考慮是否可以透過策略模式來解決。策略模式是if,switch條件分支的殺手。
//b)演算法可以被複用。
//c)策略模式可以看成是類繼承的一種替代方案。透過為環境類物件指定不同的策略,就可以改變環境類物件的行為。
//策略類的缺點:
//a)導致引入許多新策略類;
//b)使用策略時,呼叫者(main主函式)必須熟知所有策略類的功能並根據實際需要自行決定使用哪個策略類。
#include <iostream>
#include "Fighter.h"
#include "ItemStrategy.h"

#ifdef _DEBUG   //只在Debug(除錯)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定義new運算子
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{    
    class M_Undead //亡靈類怪物
    {
    public:
        void getinfo()
        {
            cout << "這是一隻亡靈類怪物" << endl;
        }
        //......其他程式碼略
    };

    class M_Element //元素類怪物
    {
    public:
        void getinfo()
        {
            cout << "這是一隻元素類怪物" << endl;
        }
        //......其他程式碼略
    };

    class M_Mechanic //機械類怪物
    {
    public:
        void getinfo()
        {
            cout << "這是一隻機械類怪物" << endl;
        }
        //......其他程式碼略
    };

    //戰士主角
    class F_Warrior
    {
    public:
        void attack_enemy_undead(M_Undead* pobj) //攻擊亡靈類怪物
        {
            //進行攻擊處理......
            pobj->getinfo(); //可以呼叫亡靈類怪物相關的成員函式
        }

    public:
        void attack_enemy_element(M_Element* pobj) //攻擊元素類怪物
        {
            //進行攻擊處理......
            pobj->getinfo(); //可以呼叫元素類怪物相關的成員函式
        }

        //其他程式碼略......
    };
}
namespace _nmsp2
{
    class Monster //作為所有怪物類的父類(抽象層)
    {
    public:
        virtual void getinfo() = 0; //純虛擬函式
        virtual ~Monster() {} //做父類時解構函式應該為虛擬函式
    };
    class M_Undead :public Monster//亡靈類怪物
    {
    public:
        virtual void getinfo()
        {
            cout << "這是一隻亡靈類怪物" << endl;
        }
        //......其他程式碼略
    };

    class M_Element :public Monster//元素類怪物
    {
    public:
        virtual void getinfo()
        {
            cout << "這是一隻元素類怪物" << endl;
        }
        //......其他程式碼略
    };

    class M_Mechanic :public Monster//機械類怪物
    {
    public:
        virtual void getinfo()
        {
            cout << "這是一隻機械類怪物" << endl;
        }
        //......其他程式碼略
    };

    //戰士主角
    class F_Warrior
    {
    public:
        void attack_enemy(Monster* pobj) //攻擊怪物
        {
            //進行攻擊處理......
            pobj->getinfo(); //可以呼叫怪物相關的成員函式
        }
        //其他程式碼略......
    };
}

int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程式退出時檢測記憶體洩漏並顯示到“輸出”視窗
        
    //第4章 策略(Strategy)模式
    //(1)一個具體實現範例的逐步重構
    //補血道具(藥品):
      //a:補血丹:補充200點生命值
      //b:大還丹:補充300點生命值
      //c:守護丹:補充500點生命值
    //Fighter,F_Warrior,F_Mage
    //策略  設計模式的定義:定義一系列演算法(策略類),將每個演算法封裝起來,讓它們可以相互替換。換句話說,策略模式通常把一系列演算法
       // 封裝到一系列具體策略類中來作為抽象策略類的子類,然後根據實際需要使用這些子類。
    //策略類中的三種角色
    //a)Context(環境類):該類中維持著一個對抽象策略類的指標或引用。這裡指Fighter類。
    //b)Stategy(抽象策略類):定義所支援的演算法的公共介面,是所有策略類的父類。這裡指ItemStrategy類。
    //c)ConcreteStrategy(具體策略類):抽象策略類的子類,實現抽象策略類中宣告的介面。這裡指ItemStrategy_BXD、ItemStrategy_DHD、ItemStrategy_SHD。
    //策略類的優點:
    //a)以擴充套件的方式支援對未來的變化,符合開閉原則。
      //遇到大量不穩定的if條件分支 或者switch分支,就要優先考慮是否可以透過策略模式來解決。策略模式是if,switch條件分支的殺手。
    //b)演算法可以被複用。
    //c)策略模式可以看成是類繼承的一種替代方案。透過為環境類物件指定不同的策略,就可以改變環境類物件的行為。
    //策略類的缺點:
    //a)導致引入許多新策略類;
    //b)使用策略時,呼叫者(main主函式)必須熟知所有策略類的功能並根據實際需要自行決定使用哪個策略類。

    //(2)依賴倒置原則:Dependency Inversion Principle,簡稱DIP
     //是面向獨享設計的主要實現方法,同時 也是實現開閉原則的重要實現途徑。
    //解釋:高層元件不應該依賴於低層(具體實現類),兩者都應該依賴於抽象層。
    //範例:工廠模式時,亡靈類M_Undead,元素類M_Element,機械類M_Mechanic。








    /*
    Fighter* prole_war = new F_Warrior(1000, 0, 200); //這裡沒有采用工廠模式,如果主角很多,可以考慮採用工廠模式建立物件
    prole_war->UseItem(LF_DHD);

    delete prole_war;     
    */

    /*
    //建立主角
    Fighter* prole_war = new F_Warrior(1000, 0, 200);

    //吃一顆大還丹
    ItemStrategy* strategy = new ItemStrategy_DHD(); //建立大還丹策略
    prole_war->SetItemStrategy(strategy); //主角設定大還丹策略,準備吃大還丹
    prole_war->UseItem(); //主角吃大還丹

    //再吃一顆補血丹
    ItemStrategy* strategy2 = new ItemStrategy_BXD(); //建立補血丹策略
    prole_war->SetItemStrategy(strategy2);//主角設定補血丹策略,準備吃補血丹
    prole_war->UseItem(); //主角吃補血丹

    delete strategy;
    delete strategy2;
    delete prole_war;
    */

    /*
    _nmsp1::M_Undead* pobjud = new _nmsp1::M_Undead();
    _nmsp1::F_Warrior* pobjwar = new _nmsp1::F_Warrior();
    pobjwar->attack_enemy_undead(pobjud); //攻擊一隻亡靈類怪物

    _nmsp1::M_Element* pobjelm = new _nmsp1::M_Element();
    pobjwar->attack_enemy_element(pobjelm); //攻擊一隻元素類怪物

    //資源釋放
    delete pobjwar;
    delete pobjud;
    delete pobjelm;*/

    _nmsp2::Monster* pobjud = new _nmsp2::M_Undead();
    _nmsp2::F_Warrior* pobjwar = new _nmsp2::F_Warrior();
    pobjwar->attack_enemy(pobjud); //攻擊一隻亡靈類怪物

    _nmsp2::Monster* pobjelm = new _nmsp2::M_Element();
    pobjwar->attack_enemy(pobjelm); //攻擊一隻元素類怪物

    //資源釋放
    delete pobjwar;
    delete pobjud;
    delete pobjelm;
    
    return 0;
}

相關文章