策略模式(Strategy Pattern):定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
策略模式是一个相对比较简单的模式,优势是把程序段中的不确定因素内聚处理。起初是用来在不同需求之间选用不同算法来进行处理。能够很好的迎合客户需求的变化,使代码有更好的维护性和扩展性。此模式最大限度采取组合(composition)替代继承的方法,使建立的系统具有很大的弹性,不仅可以将算法族封装成类,更甚可以在运行时实现动态的行为改变。而这种模式的缺点也很明显,对策略类的抽象势必会造成很多策略类。如果对这种类的处理稍有不慎就会使整个类关系紊乱。另外一个缺点是客户端必须知道所有的策略类,安全性尚且不谈,对客户端软件规模就是一种挑战。
举个例子:
一个动作冒险游戏项目,游戏中会出现各种人物(King,Queen),装备各种武器,有的是剑(sword),有的是(knife)、、、、、、、、、
1. 首先对这个需求进行分析
public class Character{
int experience;
public void fight(); //人物攻击
public void condition(); //人物的状态,这里只有武器。
}
看到这个需求时,一般的程序员脑海里马上就有了想法。先建立一个人物基类,然后,抽象出人物的行为方法。
然后,让King,Queen。。。。等分别继承Character类就好了。大功告成
但是,可怕的问题发生了。。。。。客户需求变化
骑士(Knigh)到了十级以后怎么能没坐骑呢。好,没有难度,在character类里面加入下面的方法:
public void rideHorse(); //人物的坐骑(黄骠马,大宛马,汗血马、、、o(╯□╰)o)
问题又来了,你让王后(Queen)也骑个马也太不伦不类了吧、、、、、
好,继续,用interface,只让会骑马的人物来实现rideHorse函数。那只能说是个超笨的idea,这样使重复代码变的超级多。如果Knight又升级了,要骑天龙,一个个去改去吧。
2. 再分析
并不是所有的子类都具有rideHorse行为,而且子类的Weapon人物不同也不尽相同。这时使用继承就不能很好的解决问题,因为行为总是在变化。接口也是不错,但是接口不具有实现的代码,无法达到代码复用的结果。
问题已经明确了,每次一有需求改变,或者有新的需求进来,都会使某方面的代码发生变化,需要做的就是把这段代码变化的部分抽象出来。
3. 再设计
首先,人物的武器会变化,所以把weapon抽象出来,完全脱离Character类。
//对武器状态抽象出来的接口
public interface weaponBehavior(){
public void fight();
}
各种武器的实现。
//对各种武器的实现
//knife
public class knifeBehavior implements weaponBehavior{
public void fight(){
//实现刀攻击的方法
System.out.println("Fight with Knife.");
}
}
//Axe
public class axeBehavior implements weaponBehavior{
public void fight(){
//实现斧攻击的方法
System.out.println("Fight with Axe.");
}
}
//Sword
public class swordBehavior implements weaponBehavior{
public void fight(){
//实现剑攻击的方法
System.out.println("Fight with Sword.");
}
}
//Bow
public class bowBehavior implements weaponBehavior{
public void fight(){
//实现弓箭攻击的方法
System.out.println("Fight with Bow.");
}
}
对人物基类的设计。
public abstract class Character{
weaponBehavior weapon;
public Character(){}
public abstract void fight(){
//委托行为类
weapon.fight();
}
public condition();
}
各种人物的实现。
//King的实现
public class King extends Character{
public King(){
weapon = new swordBehavior();
}
public conditon(){
System.out.println("My weapon is Sword.");
}
}
//Queen的实现
public class Queen extends Character{
public Queen(){
weapon = new knifeBehavior();
}
public conditon(){
System.out.println("My weapon is Knife.");
}
}
//Knight的实现
public class Knight extends Character{
public Knight(){
weapon = new bowBehavior();
}
public conditon(){
System.out.println("My weapon is Bow.");
}
}
//Troll的实现
public class Troll extends Character{
public Troll(){
weapon = new axeBehavior();
}
public conditon(){
System.out.println("My weapon is Axe.");
}
}
最后编写测试类来进行测试。
public class Test(){
public static void main(String args[]){
Character King = new King();
King.fight();
King.conditon();
}
}
这样基本上实现了需求。
策略模式也可以用来动态的设定行为。比如在这里给人物换装备。
在Character基类中加入一个新方法
//实现给人物换装备的功能,要传入一个装备。
public void setWeaponBehavior(weaponBehavior wb){
weapon = wb;
}
再进行测试
public class Test(){
public static void main(String args[]){
Character King = new King();
King.fight();
King.conditon();
King.setWeaponBehavior(axeBehavior);
King.fight();
}
}
这样整个项目就完成了,骑马功能还没处理好,有空再写吧,或者交给别人,困死了,如果不出什么意外的话就差不多可以交付了。
到这里才觉得发现这个例子举的其实不是很好,到后面发现不光对攻击动作可以抽象,对武器其实也应该抽象。可以把武器设置成一个参数传入figh和condition方法中。这样会使代码的整体性更强。但是那属于装饰者模式的范畴了。
回头想想,只做了两件事,对可能变化的部分进行封装,放在一起,把它从继承中挖割出来。第二件事是把继承转换成组合。这种模式对需求变化性问题的解决是很好的key。
OO设计原则:一)多用组合,少用继承。
二)针对接口编程,而不是针对实现编程。
三)找出应用中可能需要变化的地方,把他们独立出来,不要和不需要变化的代码放在一起。(封装变化)
四)松耦合,高内聚