今天咱们来看一下策略模式。在真正介绍策略模式以前呢,咱们先经过一个很是生动有趣的例子的引入。算法
咱们如今有一个模拟鸭子的程序。编程
一、鸭子父类以下:ide
public abstract class Duck { /** * 呱呱叫行为(全部鸭子都会) */ public void quack() { System.out.println("呱呱叫"); } /** * 游泳行为(全部鸭子都会) */ public void swim() { System.out.println("游泳"); } /** * 每一个鸭子额外观都不一样 因此display是抽象方法 */ abstract void display(); }
MallardDuck子类测试
public class MallardDuck extends Duck { /** *头是绿色的 */ @Override void display() { System.out.println("外观是绿头的鸭子"); } }
RedheadDuck子类this
public class RedheadDuck extends Duck { /** * 外观是红头 */ @Override void display() { System.out.println("外观是红头的鸭子"); } }
二、如今呢,有一个需求,须要让鸭子具有会飞的功能。因此,理所固然的就给Duck父类添加上了一个fly()方法;可是这里问题出现了,一些不具有会飞行为的鸭子也具有了“飞”的行为(好比,咱们如今有一个橡皮鸭子,它不会飞,而且不会呱呱叫,只会吱吱叫)。spa
三、这时候,咱们想到,能够在重写橡皮鸭子的fiy()方法,让它什么都不作,并重写quack()方法,让它吱吱叫。代码以下:设计
public class RubberDuck extends Duck { /** * 不会飞 什么都不作 */ @Override public void display() { System.out.println(""); } /** * 吱吱叫(不会 呱呱叫) */ @Override public void quack() { System.out.println("吱吱叫"); } }
四、但是,若是之后咱们加入诱饵鸭(DecoyDuck),它是一只假鸭子,不会飞也不会叫。难道咱们要接着重写方法吗?code
五、利用接口如何?那好,加入咱们利用接口,把fly()方法从超类中取出来,放进“Fiyable”接口中。这么一来,只有会飞的鸭子才实现此接口,一样的方式,也能够设计一个“Quackable”接口,由于并非全部的鸭子都会叫。orm
用这种方法确实解决了一部分的问题(不会再有会飞的橡皮鸭子),可是却形成代码没法被复用(由于每个子类都须要实现接口的方法)。对象
如今咱们想一下,会不会有一种对既有的代码影响最小的方式来修改程序?
六、如今咱们有一个设计原则:找出应用中可能须要变化之处,把它们独立出来,不要和那些不须要变化的代码混在一块儿。以此让咱们的代码变化更少,系统更加有弹性。
七、为了让程序有弹性,而且咱们还想可以指定行为到鸭子的实例,让鸭子能够在“运行时”改变“鸭子”的行为。从如今开始,鸭子的行为将被放在分开的类中,此类专门提供某行为的接口的实现。这样,鸭子就再也不须要知道行为的实现细节。(设计原则:针对接口编程,而不是针对实现编程)。
因此,咱们先声明两个接口,FlyBehavior和QuackBehavior,而行为的每一个实现都将实现其中的一个接口。直接上代码。
FlyBehavior(“飞”行为接口):
public interface FlyBehavior { /** * 飞(全部的新的飞行类都必须实现fly方法) */ void fly(); }
用翅膀飞类:
public class FlyWithWings implements FlyBehavior { @Override public void fly() { System.out.println("用翅膀来飞行"); } }
不会飞类:
public class FlyNoWay implements FlyBehavior { /** * 不会飞 */ @Override public void fly() { System.out.println(""); } }
QuackBehavior(“叫”行为接口):
public interface QuackBehavior { /** * 呱呱叫行为(每个新的叫行为都必须实现quack方法) */ void quack(); }
呱呱叫行为类:
public class Quack implements QuackBehavior { /** * 呱呱叫 */ @Override public void quack() { System.out.println("呱呱叫"); } }
吱吱叫行为类:
public class Squeak implements QuackBehavior { /** * 吱吱叫 */ @Override public void quack() { System.out.println("吱吱叫"); } }
不会叫类:
public class MuteQuack implements QuackBehavior { /** * 不会叫 */ @Override public void quack() { System.out.println(""); } }
八、下面,咱们来从新改写鸭子父类:
public abstract class Duck { FlyBehavior flyBehavior; QuackBehavior quackBehavior; /** * 游泳行为(全部鸭子都会) */ public void swim() { System.out.println("游泳"); } /** * 每一个鸭子额外观都不一样 因此display是抽象方法 */ public abstract void display(); /** * 飞行为 */ public void performFly() { flyBehavior.fly(); } /** * 呱呱叫行为 */ public void performQuack() { quackBehavior.quack(); } /** * 设置飞行为 * @param flyBehavior */ public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } /** * 设置叫行为 * @param quackBehavior */ public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } }
重写绿头鸭类:
public class MallardDuck extends Duck { public MallardDuck() { quackBehavior = new Quack(); flyBehavior = new FlyWithWings(); } /** *头是绿色的 */ @Override public void display() { System.out.println("外观是绿头的鸭子"); } }
经过咱们的改写,会发现,咱们的绿头鸭子在new一个对象的时候,会呱呱叫,会用翅膀飞,咱们还能够运行时改写它的飞行为的方式,能够把它设为不会飞,或者咱们在写一个FlyBehavior的实现类,用另一种方式来飞,这些都是能够的。
咱们写一个测试类:
public static void main(String[] args) { Duck mallardDuck = new MallardDuck(); mallardDuck.performFly(); mallardDuck.setFlyBehavior(new FlyNoWay()); mallardDuck.performFly(); }
执行结果:
九、最后,咱们会发现,若是再来新的需求,咱们不须要改变原来的任何代码,好比来了一只会用火箭飞的鸭子,那咱们只须要新写一个FlyBehavior的实现类,而后在构造器里写出来就好了。对修改关闭,对扩展开放。
十、如今 咱们已经学会了策略模式。哈哈。策略模式:定义了算法族,分别封装起来,让他们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。
最后来个总结:
意图:定义一系列的算法,把它们一个个封装起来, 而且使它们可相互替换。
主要解决:在有多种算法类似的状况下,使用 if...else 所带来的复杂和难以维护。
什么时候使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例: 一、诸葛亮的锦囊妙计,每个锦囊就是一个策略。 二、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 三、JAVA AWT 中的 LayoutManager。
优势: 一、算法能够自由切换。 二、避免使用多重条件判断。 三、扩展性良好。
缺点: 一、策略类会增多。 二、全部策略类都须要对外暴露。
使用场景: 一、若是在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式能够动态地让一个对象在许多行为中选择一种行为。 二、一个系统须要动态地在几种算法中选择一种。 三、若是一个对象有不少的行为,若是不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:若是一个系统的策略多于四个,就须要考虑使用混合模式,解决策略类膨胀的问题。