C++设计模式_行为型模式_策略模式Strategy
策略模式是一种行为型模式,其实现过程与模板方法模式非常类似,都是通过以扩展的方式支持未来的变化。
一个具体实现范例的逐步重构
场景:主角要补充血量,现在要设置三种补血的药丸,分别是补血丹,补充200点生命值,大还丹,补充300生命值,守护丹:补充500点生命值;
创建fighter.h,在其中创建Fighter类—战斗者父类,然后实现战士类和法师类,继承自战斗者父类。
enum class ItemMedicine
{LIFE_BXD, // 补血丹LIFE_DHD, // 大还丹LIFE_SHD, // 守护丹
};class Fighter
{
public:Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}// 定义一个吃药的函数// 这个函数的参数是一个枚举类型,这个枚举类型定义了各种各样的药品,比如补血丹、大还丹、守护丹等等。void useItem(ItemMedicine type){if (type == ItemMedicine::LIFE_BXD) //道具类型:补血丹{m_life += 200;//补充200点生命值cout << "补充了补血丹" << endl;}else if (type == ItemMedicine::LIFE_DHD) //道具类型:大还丹{m_life += 300;//补充300点生命值cout << "补充了大还丹" << endl;}else if (type == ItemMedicine::LIFE_SHD) //道具类型:守护丹{m_life += 500;//补充500点生命值cout << "补充了守护丹" << endl;}//.......其他的一些判断逻辑,略。。。。。。}protected:int m_life;int m_magic;int m_attack;
};class F_Warrior :public Fighter
{
public:F_Warrior(int life, int magic, int attack) :Fighter(life, magic, attack) {}};;class F_Mage : public Fighter
{
public:F_Mage(int life, int magic, int attack) :Fighter(life, magic, attack) {}private:};
上面代码缺点:
如果要增加新的道具,比如 补魔法丹,那么就要在Fighter类中UseItem函数中,增加if else语句,这样就违背了开闭原则—对扩展开放,对修改关闭。
如果怪物也要吃补充生命值的药物,那么就要在Monster类中也增加UseItem函数,这样就违反了单一职责原则,这属于代码级别的重复。
如果主角吃药后,还有其他的一些额外的逻辑,比如吃药后从中毒状态变为正常状态,还要在useItem函数中增加判断语句;同样,如果主角处于狂暴状态,血量要额外增加。这样也 违反的开闭原则。
比如实现吃药后主角从中毒变为正常,需要修改如下:
void useItem(ItemMedicine type){if (type == ItemMedicine::LIFE_BXD) //道具类型:补血丹{m_life += 200;//补充200点生命值cout << "补充了补血丹" << endl;// if (主角中毒){// 停止中毒状态,也就是主角吃药后不再中毒}if (主角处于狂暴状态){m_life += 400; //额外再补充400点生命值m_magic += 200; //魔法值也再补充200点}}else if (type == ItemMedicine::LIFE_DHD) //道具类型:大还丹{m_life += 300;//补充300点生命值cout << "补充了大还丹" << endl;}else if (type == ItemMedicine::LIFE_SHD) //道具类型:守护丹{m_life += 500;//补充500点生命值cout << "补充了守护丹" << endl;}//.......其他的一些判断逻辑,略。。。。。。}
使用策略模式对代码重构
策略模式的定义:定义一系列算法(策略类),将每个算法封装起来,让它们可以相互替换。换句话说,策略模式通常把一系列算法(函数)封装到一系列具体策略类中来作为抽象策略类的子类,然后根据实际需要使用这些子类。
策略类中三种角色:
环境类:上面代码中的Fighter类,该类中维持着一个对抽象策略类的指针或引用。
抽象策略类:定义所支持的算法的公共接口,是所有策略类的父类,这里指ItemStrategy
具体策略类:抽象策略类子类,实现抽象策略类中声明的接口,这里指ItemStrategy_BXD ItemStrategy_HHD;
class Fighter;class ItemStrategy{public:virtual void useItem(Fighter* Fighter) = 0;virtual ~ItemStrategy(){}protected:Fighter* m_pFighter = nullptr;};class ItemStrategy_BXD : public ItemStrategy{public:void useItem(Fighter* fighter) override; // 声明,稍后定义};class ItemStrategy_HHD : public ItemStrategy{public:void useItem(Fighter* fighter) override; // 声明,稍后定义};class Fighter{public:Fighter() {}Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}virtual ~Fighter() {}public:void SetLift(int life){m_life = life;}int GetLife(){return m_life;}void SetItemStrategy(ItemStrategy* itemStrategy){m_itemStrategy = itemStrategy;}void UsetItem(){m_itemStrategy->useItem(this);}protected:int m_life;int m_attack;int m_magic;ItemStrategy* m_itemStrategy = nullptr;};// 战士类class Warrior : public Fighter{public:using Fighter::Fighter;};// 将实现放到 Fighter 定义之后inline void ItemStrategy_BXD::useItem(Fighter* fighter){fighter->SetLift(fighter->GetLife() + 100);}inline void ItemStrategy_HHD::useItem(Fighter* fighter){fighter->SetLift(fighter->GetLife() + 80);}void test(){Fighter* fighter = new Warrior(100, 20, 300);// 实例化一个对象ItemStrategy* ItemStrategy = new ItemStrategy_BXD();fighter->SetItemStrategy(ItemStrategy);fighter->UsetItem();}
策略类优点:
1 以扩展的方式支持对未来的变化,符合开闭原则。遇到大量不稳定的if条件分支 或者switch分支,就要优先考虑是否可以通过策略模式来解决。策略模式是if,switch条件分支的杀手。
2 算法可以被复用。
3 策略模式可以看成是类继承的一种替代方案。通过为环境类对象指定不同的策略,就可以改变环境类对象的行为。比如,战士对象吃大还丹。
策略类的缺点:
1 导致引入许多新策略类;
2 使用策略时,调用者(main主函数)必须熟知所有策略类的功能并根据实际需要自行决定使用哪个策略类。
UML图:
组合关系:Gighter中有straty类的指针。
依赖倒置原则
依赖倒置原则:Dependency Inversion Principle 简称DIP,是面向独享设计的主要实现方法,同时也是实现开闭原则的重要实现途径。
解释:高层组建不应该依赖于底层组建,比如之前的主角(战士,法师)类就是高层组建,怪物类(亡灵类,机械类等)就是底层组建,高层组建不能直接依赖于底层组建,举例:
namespace _nmsp1
{class M_Undead{public:void getinfo(){cout << "M_Undead" << endl;}};class M_Element{public:void getinfo(){cout << "M_Element" << endl;}};class M_Mechnical{public:void getinfo(){cout << "M_Mechnical" << endl;}};// 战士主角class Fighter_Warrior{public:// 攻击一种怪物,对应增加一个攻击怪物的函数void attack_enemy_undead(M_Undead *p){cout << "attack_enemy" << endl;}void attack_enemy_mechnical(M_Mechnical* p){cout << "M_Mechnical" << endl;}};
}
int main()
{// 创建一个高层类_nmsp1::Fighter_Warrior *pWar = new _nmsp1::Fighter_Warrior();// 创建一个低层类_nmsp1::M_Undead* p1 = new _nmsp1::M_Undead();// 调用高层类的函数 实现攻击怪物目的pWar->attack_enemy_undead(p1);// 战士继续攻击另外一种怪物_nmsp1::M_Mechnical* p2 = new _nmsp1::M_Mechnical();pWar->attack_enemy_mechnical(p2);return 0;
}
从上图可以看出,高层组建依赖于底层组建。
改进:
要实现高层组建和底层组建的解耦合,只需要增加一个Monster 类,代码如下:
namespace _nmsp2
{class Monster{public:Monster() {}virtual ~Monster(){}};class M_Undead : public Monster{public:void getinfo(){cout << "M_Undead" << endl;}};class M_Element : public Monster{public:void getinfo(){cout << "M_Element" << endl;}};class M_Mechnical : public Monster{public:void getinfo(){cout << "M_Mechnical" << endl;}};// 战士主角class Fighter_Warrior{public:// 攻击一种怪物,对应增加一个攻击怪物的函数void attack_enemy(Monster* p){cout << "attack_enemy" << endl;}};
}int main()
{_nmsp2::Fighter_Warrior *pWar2 = new _nmsp2::Fighter_Warrior();// 亡灵类怪物_nmsp2::Monster* p1 = new _nmsp2::M_Undead();pWar2->attack_enemy(p1);// 攻击亡灵类_nmsp2::Monster* p2 = new _nmsp2::M_Mechnical();pWar2->attack_enemy(p2);// 攻击亡灵类return 0;
}