以下为策略模式详解:
引子:
使用策略就是要实现可扩展性,那么多态是不可少的。何谓可扩展性呢?
比如:我们用面向对象的思想来设计飞机,基类为飞机,飞机可以有很多种,客机,直升机,战斗机等,不同种类的飞机起飞方式和武器都不一样,那么在设计时怎样才能做到支持所有的飞机,每次加入新的种类的飞机时不要去改动之前的代码直接加入呢?
这就是我们使用策略模式要考虑的问题,如果只用继承,显然不行,这样太死板,像客机它是没有武器的,当在基类中加入了使用武器方法后,客机也具有了武器,这不科学,而且直升机和战斗机的武器不同还要强迫每一个子类都复写使用武器方法。只用接口呢?也是不行的,比如写一个武器接口,只面有一个使用武器的方法,谁需要安装武器就实现这个接口,这样好像比继承要好一点,但有没有发现,代码的可重用性太差,重复代码会变多,一样不科学。如果我们在写代码时达到不必知道子类的具体类型,只要系统在使用武器时知道使用何种武器,那么这个问题就可以解决,怎么做到呢?
针对超类型编程,父类的引用指向子类的对像,这样就我们就可以驱动必不可少的多态。
我们可以这样设计:先设置几个接口,轻武器接口,重武器接口,缓冲起飞接口,垂直起飞接口(完整代码在最后在下载地址):
/** * @author Homg */ public interface Weapon { void fire(); } /** * @author Homg */ public interface TakeOff { void takeOff(); }
在基类中加入行为变量,申明为对应接口类型,在使用武器和起飞时委托给实例变量引用的对象来具体处理:
private TakeOff takeOff; private Weapon weapon; public void fire() { if (weapon != null) { weapon.fire(); } } public void takeOff() { if (takeOff != null) { takeOff.takeOff(); } }
然后子类(战斗机或客机)如果需要起飞和使用武器就要给对应的行为变量赋值,也就是实例化这个继承而来的行为变量。还有个问题就是飞机有可能需要灵活的更换武器系统啊,那怎么办?我们可以这样设计:使用set方法来随时,灵活的改变接口类型的实例变量,当要换武器时只需要set一下就好:
public void setTakeOff(TakeOff takeOff) { this.takeOff = takeOff; }public void setWeapon(Weapon weapon) { this.weapon = weapon; }
入口为这样:
CopterTakeOff copterTakeOff=new CopterTakeOff(); CopterWeapon copterWeapon =new CopterWeapon(); Plane plane=new Copter(); plane.setTakeOff(copterTakeOff); plane.setWeapon(copterWeapon);
plane.sayName(); plane.takeOff(); plane.fire();
运行结果:
我是直升机,垂直起飞,使用30毫米机炮开火
入口换成这样:
PassengerPlaneTakeOff passengerPlaneTakeOff=new PassengerPlaneTakeOff(); Plane plane2=new PassengerPlane(); plane2.setTakeOff(passengerPlaneTakeOff); plane2.sayName(); plane2.takeOff(); plane2.fire();
运行结果:
我是客机,滑动起飞,
客机是没有武器的,所以没有使用武器开火。
类图:
应用:
想一下,java封装的Comparator,是不是也用的策略模式?我们先要实现一个comparator接口,再把他传入Collections.sort(List<T>list,Comparator<? superT> c)中。
还有android中的异步处理(AsyncTask),使用回调接口,这是不是也是策略模式?
总结:
这就是策略模式,定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
分离出可能变化的部分,用一个处理器来处理这些变化,降低系统耦合度,不会牵一发而动全身。
在设计时要多用组合,少用继承,组合建立的系统有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态地改变行为,只要组合的行为对象符合正确的接口标准即可。
完整代码下载地址(也可以留下邮箱发给你):