
设计模式之策略模式
什么是策略模式 ?
- 定义一系列算法,封装每个算法,并使它们可互换。策略模式使算法独立于使用该算法的客户端而变化。
先简单的模拟鸭子的应用做起
系统的内部设计使用标准的OOP技术,设计了一个鸭子的超类(Superclass),并让各种鸭子继承此超类。
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
|
public abstract class AbstractDuck {
protected void quack() { System.out.println("嘎嘎叫..."); }
protected void swim() { System.out.println("游泳..."); }
protected abstract void display(); }
|
1 2 3 4 5 6 7 8 9
|
public class MallardDuck extends AbstractDuck { @Override protected void display() { System.out.println("头是绿色的."); } }
|
1 2 3 4 5 6 7 8 9
|
public class RedheadDuck extends AbstractDuck { @Override protected void display() { System.out.println("头是红色的."); } }
|
后来新增了需求,让鸭子飞起来。于是在Duck超类加上fly()方法,然后所有的鸭子继承fly()。
1 2 3 4 5 6 7 8 9 10
|
public abstract class AbstractDuck { protected void fly() { System.out.println("鸭子飞起来..."); } }
|
但是,可怕的问题发生了….
由于并发所有的Duck类都会飞,在超类加上新的行为,会使得某些并不合适该行为的子类也具有该行为。当涉及维护时,为了复用目的而使用继承并不完美。
利用接口改造
可以把fly()从超类中提取出来,放进“Flyable接口”中,这么一来,只有会飞的鸭子才实行此接口。同样方式,也可以用设计一个“Quackable接口”,因为不是所有的鸭子都会叫。
1 2 3 4 5 6
|
public interface Flyable { void fly(); }
|
1 2 3 4 5 6
|
public interface Quackable{ void quack(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public abstract class AbstractDuck {
protected void swim() { System.out.println("游泳..."); }
protected abstract void display(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class MallardDuck extends AbstractDuck implements Flyable, Quackable { @Override protected void display() { System.out.println("头是绿色的."); }
@Override public void quack() { System.out.println("嘎嘎叫..."); }
@Override public void fly() { System.out.println("飞行..."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class RedheadDuck extends AbstractDuck implements Flyable, Quackable { @Override protected void display() { System.out.println("头是红色的."); }
@Override public void quack() { System.out.println("嘎嘎叫..."); }
@Override public void fly() { System.out.println("飞行..."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class RubberDuck extends AbstractDuck implements Quackable { @Override protected void display() { System.out.println("橡皮鸭"); }
@Override public void quack() { System.out.println("橡皮鸭吱吱叫..."); } }
|
1 2 3 4 5 6 7 8 9
|
public class DecoyDuck extends AbstractDuck { @Override protected void display() { System.out.println("诱饵鸭五颜六色"); } }
|
我们知道,并非所有的子类都具有飞行和呱呱叫的行为,所以“继承”并不是适当的解决方式。虽然Flyable和Quackable可以解决一部分问题,但是造成代码无法复用,而且对于很多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
|
public abstract class AbstractDuck {
protected FlyBehavior flyBehavior;
protected QuackBehavior quackBehavior;
public void performQuack() { quackBehavior.quack(); }
public void performFly() { flyBehavior.fly(); }
protected void swim() { System.out.println("All ducks float, even decoys..."); }
protected abstract void display();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public interface FlyBehavior { void fly(); }
public class FlyWithWings implements FlyBehavior { @Override public void fly() { System.out.println("I'm flying..."); } }
public class FlyNoWay implements FlyBehavior { @Override public void fly() { System.out.println("I can't 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 25 26 27 28
|
public interface QuackBehavior { void quack(); }
public class Squeak implements QuackBehavior { @Override public void quack() { System.out.println("squeak"); } }
public class Quack implements QuackBehavior { @Override public void quack() { System.out.println("quack"); } }
public class MuteQuack implements QuackBehavior { @Override public void quack() { System.out.println("silence"); } }
|
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
| public class MallardDuck extends AbstractDuck {
public MallardDuck() { super.quackBehavior = new Quack(); super.flyBehavior = new FlyWithWings(); }
@Override protected void display() { System.out.println("I'm a green head duck."); } }
public class ModelDuck extends AbstractDuck {
public ModelDuck() { super.flyBehavior = new FlyNoWay(); super.quackBehavior = new Quack(); }
@Override protected void display() { System.out.println("I'm a model duck."); } }
|
动态设定行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public abstract class AbstractDuck {
public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; }
public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; }
}
|
运行结果
1 2 3 4 5 6 7 8 9
| public class MiniDuckSimulator { public static void main(String[] args) { AbstractDuck duck = new MallardDuck(); duck.performFly(); duck.setQuackBehavior(new Squeak()); duck.performQuack(); } }
|
在运行时改变行为,只需要调用父类的setter方法就可以了。我们通过策略模式来应对任何的改变,使得程序变得更简易健壮。
设计原则
多用组合,少用继承。
总结
策略模式,定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。