首先什么是“策略模式”:定义了算法家族,分别封装起来,让他们之间可以相互替换,让算法的变化,不会影响到使用算法的客户。(大话设计模式P23)
我们先来看看策略模式的UML类图是怎样的。
1、其中Context类利用ConcreteStrategy类来配置,维护Strategy的引用
2、Strategy类是算法的公共接口
3、ConcreteStrategy类则实现了算法的行为
注意:其中Context类和Strategy类是一种“聚合关系”,是一种弱的拥有关系,其与组合关系的区别是:组合关系是一种强的拥有关系,组合关系中相互之间的生命周期是相同的。而聚合关系则不要求,我的理解是:Context可以有Strategy,但是即使不存在Context,Strategy本身也是可以存在的。其中的细微差别需要好好品味。
代码实现:
为简便我省略掉头文件和命名空间的声明。
//Context class Context { public: Context(Strategy* s) : strategy(s) {} void ContextInterface() { strategy->AlgorithmInterface(); } private: Strategy *strategy; };
//Strategy class Strategy { public: virtual void AlgorithmInterface() = 0; }; //ConcreteStrategyA class ConcreteStrategyA : public Strategy { public: void AlgorithmInterface() { //do A } }; //ConcreteStrategyB class ConcreteStrategyB : public Strategy { public: void AlgorithmInterface() { //do B } };
客户端:
int main() { //以下代码只作示范使用,实际上用new产生的对象是在heap中的 //所以第二次new的时候,第一次产生的对象不会自动删除,因此会产生内存泄露 //我们可以手动delete,或者利用智能指针来解决 Strategy *s = new ConcreteStrategyA(); Context *c = new Context(s); c->ContextInterface(); s = new ConcreteStrategyB(); c = new Context(s); c->ContextInterface(); return 0; }
下面我找个例子来试验一下:游戏中根据选择的职业不同,攻击方式也会不同
游戏角色类
//游戏角色类 class Player { public: Player(Character* c) : pChar(c) {} void action() { pChar->attack(); } private: Character *pChar; };
职业类
//职业类接口 class Character { public: virtual void attack() = 0; }; //剑士 class Saber : public Character { public: void attack() { cout << "斩击" << endl; } }; //弓箭手 class Archer : public Character { public: void attack() { cout << "射击" << endl; } }; //魔法师 class Caster : public Character { public: void attack() { cout << "火球术" << endl; } };
客户端
int main() { Player *player; cout << "魔法师攻击: "; player = new Player(new Caster()); player->action(); cout << "剑士攻击: "; player = new Player(new Saber()); player->action(); return 0; }
策略模式有助于我们应对算法行为的变化,例如上述的例子中,如果我们增加新的职业,我们只需要添加新职业的类,之后在人物当中配置新职业的类,我们就可以应对这种变化。