策略模式定义了算法族,不同的策略实现之间可以互相替换,让算法的变化独立于使用算法的客户。
这里的算法可以理解为对传入参数的处理或者某种行为。例如:如下java代码,对字符串实现不同的比较策略,Formatter类与CompareStrategy策略接口相依赖。可以使自身能够有对字符串的不同处理。
public interface CompareStrategy { boolean less(String str1, String str2); } public class SizeCmpStrategy implements CompareStrategy { @Override public boolean less(String str1, String str2) { return str1.length() < str2.length(); } } public class IgnoreCaseCmpStrategy implements CompareStrategy { @Override public boolean less(String str1, String str2) { return str1.compareToIgnoreCase(str2) < 0; } } public class Formatter { private CompareStrategy cmp; public CompareStrategy getCmp() { return cmp; } public void setCmp(CompareStrategy cmp) { this.cmp = cmp; } }
通常可以通过继承,重写父类的方法,使不同子类具有多态的行为。策略模式可以提供了替代继承的另一种方法。如果我们需要具备不同功能的类,我们可以不必总是通过继承某一父类实现不同的行为。而是通过注入不同的Strategy使其可以具备不同的行为。再举一个例子:
在一个游戏的角色随着升级的过程中,不同等级的英雄有不同的攻击和防守。可以将这变化的攻击和防守进行封装。如下面的Java代码:
public interface Attack { void attack(); } public interface Defend { void defend(); } public class PrimaryAttack implements Attack { @Override public void attack() { System.out.println("初级进攻"); } } public class PrimaryDefend implements Defend { @Override public void defend() { System.out.println("初级防守"); } } public class SeniorAttack implements Attack { @Override public void attack() { System.out.println("高级进攻"); } } public class SeniorDefend implements Defend { @Override public void defend() { System.out.println("高级防守"); } } public class Hero { private Attack attack; public Attack getAttack() { return attack; } public void setAttack(Attack attack) { this.attack = attack; } private Defend defend; public Defend getDefend() { return defend; } public void setDefend(Defend defend) { this.defend = defend; } public void doAttack() { attack.attack(); } public void doDefend() { defend.defend(); } public static void main(String[] args){ Hero hero=new Hero(); hero.setAttack(new PrimaryAttack()); hero.setDefend(new PrimaryDefend()); hero.doAttack(); hero.doDefend(); System.out.println("升级啦"); hero.setAttack(new SeniorAttack()); hero.setDefend(new SeniorDefend()); hero.doAttack(); hero.doDefend(); } }
//程序输出:
/*
初级进攻
初级防守
升级啦
高级进攻
高级防守
*/
由上述的例子,我们可以得出策略模式优点:这是一种可以替代继承的方法。并且这种行为是可以动态改变的并且不像继承一样硬编码。
2. C++模板和策略模式
上述Java实现,C++可以很方便的进行类似的实现。这里再介绍一下基于C++模板的策略模式实现。
C++的模板编程可以作为另外一种策略模式的实现,并且可能是更好的实现。将Strategy作为模板参数,开发出更为方便(隐式接口),高效率(编译期绑定)的程序。如:
#include <iostream> class PrimaryAttack{ public: void attack(){ std::cout<<"进行初级进攻"<<std::endl; } }; class PrimaryDefend{ public: void defend(){ std::cout<<"进行初级防守"<<std::endl; } }; class SeniorDefend{ public: void defend(){ std::cout<<"进行高级防守"<<std::endl; } }; class SeniorAttack{ public: void attack(){ std::cout<<"进行高级进攻"<<std::endl; } }; template<typename Attack, typename Defend> class Hero{ public: Hero():attack(),defend(){} void doAttack() { attack.attack(); } void doDefend(){ defend.defend(); } private: Attack attack; Defend defend; }; int main(){ std::cout<<"我是初级英雄"<<std::endl; Hero<PrimaryAttack, PrimaryDefend> primary_hero; primary_hero.doAttack(); primary_hero.doDefend(); std::cout<<"我是高级英雄"<<std::endl; Hero<SeniorAttack, SeniorDefend> senior_hero; senior_hero.doAttack(); senior_hero.doDefend(); } //程序输出: /* 我是初级英雄 进行初级进攻 进行初级防守 我是高级英雄 进行高级进攻 进行高级防守 */
Note:由于模板属于编译时多态,因此行为属于编译期绑定,不能像之前Java实现那样对策略进行动态的改变。
使用模板实现策略模式,带来了代码的简洁,不再需要定义接口,再进行实现。由于C++模板编程隐式接口约定,因此需要以文档的形式对策略的设计进行规定,如规定 Attack, Defend类型参数必须实现attack(),defend()方法。
与基于C++模板的策略模式相比,Java的实现比较繁琐,基于C++模板的实现更为轻量级。