策略模式(strategy pattern)
1.从鸭子仿真程序开始
假设有一个鸭子仿真程序,Duck类是一个抽象类,定义了鸭子应该具有的行为,鸭子应该会呱呱叫,有quack()函数;所有的鸭子都会游泳,有swim()函数。这两个函数都是实函数。由于每只鸭子都有不同的表现形式,因此display函数是一个虚函数,每个鸭子的子类都需要自己实现这个函数。
2. 添加飞翔功能 这时我们需要为鸭子添加一个飞翔的功能,因此我们需要为Duck类添加一个fly()的函数。于是类的设计就变成了下面这样。
下面是Duck的实现代码:完整的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public abstract class Duck1 { public void quack () { System.out.println("quack" ); } public void swim () { System.out.println("I can swim" ); } public abstract void display () ; public void fly () { System.out.println("I can fly" ); } }
这时问题出现了,并不是每一只鸭子都会飞,直接在Duck类中添加fly函数,意味着所有子类都具有了fly的能力。同样也不是每一只鸭子都会呱呱叫(例如一只玩具鸭子),因此Duck的子类必须重写这些函数以满足自己的场景。
例如下面这个例子,橡皮鸭(玩具)就需要重写quack和fly函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class RubberDuck extends Duck1 { @Override public void quack () { } @Override public void fly () { } @Override public void display () { System.out.println("RubberDuck" ); } public static void main (String[] args) { RubberDuck r = new RubberDuck(); r.display(); r.quack(); r.swim(); r.fly(); } }
这样设计的问题是每出现一种新的Duck的子类都需要考虑是否重写quack和fly函数,会出现大量的重复代码
3. 一种解决方案
一种解决方案是将fly和quack从Duck超类中提取出来做成两个接口Flyable和Quackable。只有拥有这些功能的子类才会去实现这些接口。
这种解决方式破坏了代码重用的原则,每一个子类都必须实现接口中的方法,后期如果需要修改,就需要打开每一个实现了接口方法的类进行修改,后期代码维护将是一个噩梦。
4. 策略模式 定义 策略模式(strategy pattern)defines a family of algorithms,encapsulates(封装) each one, and makes them interchangeable.Strategy lets the algorithms vary independently from clients that use it.
4.1. 将变化的部分与不变的部分分离 我们知道fly和quack在不同的子类中的表现是不同的,因此将这两个方法提取出来并创建一系列的类来实现每一种行为
4.2. 面向接口编程而非面向实现 我们使用一个接口来表示一种行为,例如FlyBehavior接口表示飞翔的行为;QuackBehavior接口表示鸣叫的行为。
这时Duck的子类不负责实现fly和quack的接口,我们会实现一些列的行为类来实现这些接口。接口和类的关系如下图:
这与之前的做法是完全不同的,之前的方式是或者在Duck超类中实现一个行为或者在子类中实现一个行为。在之前的做法中我们都依赖于一种实现,我们的代码就被限定与某一种特定的实现。
在新的设计中,Duck子类将会通过接口(FlyBehavior or QuackBehavior)来使用某一个行为,这样实际行为的实现就不会被限定在Duck的子类中。
接口示例代码:完整代码
下面的代码中实现了FlyBegavior接口以及两个具体飞翔行为的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public interface FlyBegavior { public void fly () ; } class FlyWithWings implements FlyBegavior { @Override public void fly () { System.out.println("I can Fly" ); } } class FlyNoWay implements FlyBegavior { @Override public void fly () { System.out.println("can't fly" ); } }
完整代码
下面的代码实现了QuackBehavior接口以及三个鸣叫的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public interface QuackBehavior { public void quack () ; } class Quack implements QuackBehavior { @Override public void quack () { System.out.println("quack" ); } } class Squeak implements QuackBehavior { @Override public void quack () { System.out.println("squeak" ); } } class MuteQuack implements QuackBehavior { @Override public void quack () { System.out.println("muteQuack" ); } }
Duck及子类示例代码:完整代码
在Duck类中定义了两个接口类型的变量,Duck并不负责实现这些接口,只是当子类创建实例时将接口的实现类作为参数传给接口变量,使得程序在执行时动态决定调用哪个具体的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public abstract class Duck2 { private FlyBegavior flyBegavior; private QuackBehavior quackBehavior; public Duck2 (QuackBehavior quackBehavior, FlyBegavior flyBegavior) { this .flyBegavior = flyBegavior; this .quackBehavior = quackBehavior; } public void performQuack () { quackBehavior.quack(); } public void swim () { System.out.println("I can siwm" ); } public abstract void display () ; public void performFly () { flyBegavior.fly(); } } class MallardDuck2 extends Duck2 { public MallardDuck2 () { super (new Quack(), new FlyWithWings()); } @Override public void display () { System.out.println("I'm a real Mallard duck" ); } public static void main (String[] args) { MallardDuck2 m = new MallardDuck2(); m.display(); m.performFly(); m.performQuack(); m.swim(); } }
5. 练习题