文章目录
策略模式具体的应用情景
假设你还是游戏程序员。游戏策划想让你实现补血的道具,主要有三种:补血丹,能够补充200生命;大还丹,补充300生命;守护丹,补充500生命。
我们在 模板方法模式 中已经实现了两种主角:战士和法师。可以在他们之中增加一个函数,用来给自己恢复生命值。
namespace hjl_project1
{
//增加补充生命值的道具
enum ItemAddLife
{
LF_BXD, //补血丹
LF_DHD, //大还丹
LF_SHD, //守护丹
};
//抽象类
class Fighter
{
public:
Fighter(int life, int magic, int attack)
: m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Fighter() {}
//使用药品的函数
void UseItem(ItemAddLife djtype)
{
if (djtype == LF_BXD)
{
cout << "使用了补血丹,增加200生命" << endl;
m_life += 200;
}
else if (djtype == LF_DHD)
{
cout << "使用了大还丹,增加300生命" << endl;
m_life += 300;
}
else if (djtype == LF_SHD)
{
cout << "使用了守护丹,增加500生命" << endl;
m_life += 500;
}
}
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) {}
};
}

上面的代码从程序的功能上来看没什么问题,但从面向对象的视角来看,就会有一些问题。
- 试想,如果想要增加新的补血药品的话,就要增加新的枚举类型,同时要增加新的if else语句,很明显不符合“开闭原则”。
- 代码的复用性很差,因为我们目前实现的功能是主角能够吃药品补血,如果后面我们想要实现npc吃药补血,或者怪物吃药补血,是不是就没法复用UseItem这一段代码了?
- 如果后面我们想给主角增加恢复魔法值的药物,就又需要修改UseItem的逻辑了,最后就会导致这段代码逻辑非常复杂。
下面我们使用策略模式对上面这段代码进行改造。
我们给各种补血药品设置一个道具类抽象父类,这些药品增加多少血量,则是由各自的类实现。而主角类则只需要设置一个道具类的指针,即可使用药品。
//Fighter.hpp
namespace hjl_project2
{
class ItemStrategy;
//抽象类
class Fighter
{
public:
Fighter(int life, int magic, int attack)
: m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Fighter() {}
//设置策略类的指针
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;
};
//战士类
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) {}
};
}
//在.cpp文件实现.hpp的成员函数
//Fighter.cpp
#include "Fighter.hpp"
#include "ItemStrategy.hpp"
void hjl_project2::Fighter::SetItemStrategy(ItemStrategy *strategy)
{
itemstrategy = strategy;
}
//使用道具
void hjl_project2::Fighter::UseItem()
{
itemstrategy->UseItem(this);
}
//获取生命值
int hjl_project2::Fighter::GetLife()
{
return m_life;
}
//设置生命值
void hjl_project2::Fighter::SetLife(int life)
{
m_life = life;
}
//ItemStrategy.hpp
#include "Fighter.hpp"
namespace hjl_project2
{
//道具策略类的父类
class ItemStrategy
{
public:
virtual void UseItem(Fighter *mainobj) = 0;
~ItemStrategy(){};
};
//补血丹的策略类
class ItemStrategy_BXD : public ItemStrategy
{
public:
void UseItem(Fighter *mainobj)
{
cout << "使用了补血丹,增加200生命值" << endl;
mainobj->SetLife(mainobj->GetLife() + 200);
}
};
//大还丹的策略类
class ItemStrategy_DHD : public ItemStrategy
{
public:
void UseItem(Fighter *mainobj)
{
cout << "使用了大还丹,增加300生命值" << endl;
mainobj->SetLife(mainobj->GetLife() + 300);
}
};
//守护丹的策略类
class ItemStrategy_SHD : public ItemStrategy
{
public:
void UseItem(Fighter *mainobj)
{
cout << "使用了守护丹,增加500生命值" << endl;
mainobj->SetLife(mainobj->GetLife() + 500);
}
};
}


策略模式定义
定义一系列算法(策略类),将每个算法封装起来,让它们可以互相替换。
也就是说,策略模式通常把一系列算法封装到一系列具体策略类中来作为抽象策略类的子类,然后根据需要来使用这些子类。
这和“依赖倒置原则”非常像。
策略模式中有三种角色:
- Context(环境类):该类中维持着一个对抽象策略类的指针或者引用,这里指Fighter类。
- Stategy(抽象策略类):定义所支持的算法的公共接口,是所有策略类的父类。
- ConcreteStrategy(具体策略类):抽象策略类的子类,实现抽象策略类的函数。
- 环境类和具体策略类,通过抽象策略类产生依赖关系,增加或者修改具体的策略类并不会引起环境类的改变。
策略模式的优点:
- 以扩展的方式支持对未来的变化,符合“开闭原则”。如果if else条件分支很多,并且不稳定的话,可以优先考虑采用策略模式。
- 具体策略类中的算法可以被复用。比如被另一个环境类使用。
- 策略模式可以被看成是类继承的一种替代方案。类继承是一种is-a关系,而策略模式是has-a的关系。
策略模式的缺点:
- 导致引入很多具体策略类。
- 使用策略时,调用者(main主函数)必须要熟知所有的具体策略类,并且给环境类设置具体策略类的指针或者引用。
本文通过一个游戏道具补血的例子,展示了如何运用策略模式来改进代码,避免使用大量的ifelse语句,提高代码的扩展性和复用性。在改造后的代码中,每个补血药品对应一个具体策略类,通过接口实现不同的补血效果,使得添加新药品或扩展其他功能时无需修改原有代码,遵循了开闭原则。
5658

被折叠的 条评论
为什么被折叠?



