本文对《Head First 设计模式》中的策划者模式进行了总结
策划者模式
问题描述: 一款模拟鸭子游戏,要求运行时能产生各种不同的鸭子(绿头鸭子,红头鸭子,橡皮鸭子),现在要求一部分鸭子具备飞行功能。
错误的做法1:
一个公司程序员采用了继承的方法,让所有鸭子继承超类Duck,然后在Duck中加上了fly(),swim(),quack()等方法,产生了可怕的结果,(橡皮鸭会飞了)。这使得一些鸭子具备了一些他们不需要的方法。
错误的做法2:
将fly()和quack()方法从Duck中取出放入Flyable()和Quackable()接口中,然后让会飞和会叫的鸭子去实现这两个接口。这样虽然解决了第一个问题,让鸭子们只拥有自己该有的方法。但是代码却没有得到复用,一旦需要修改某个行为,就必须往下追踪,并去每一个具备该行为的类中修改,当子类的数目特别多的时候,效率会特别低下。
正确的做法:
创建行为接口FlyBehavior,里面放一个抽象的fly()方法,然后创建两个飞行行为的类FlyWithWings和FlyNoWay实现FlyBehavior接口并将fly()方法具体实现。同理创建QuackBehavior接口和两个叫声行为的类Qucak和MuteQuack去实现QuackBehavior接口,实现不同的quack()方法。
这样鸭子的飞行和叫声行为就委托给了FlyBehavior和QuackBehavior。将FlyBehavior和QuackBehavior当做成员放在Duck类中(即采用 “组合” ),鸭子类中不再有Fly()和Quack()方法,而是performFly()和performQuack()方法,通过成员FlyBehavior和QuackBehavior调用Fly()和Quack()方法。
这样的话,如果某个行为的实现细节需要改变,直接去FlyBehavior和QuackBehavior的少数子类中修改就行了,而不用像之前那样挨个去修改几十种鸭子的行为。
以下是Duck的代码
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public abstract void display();
public void performQuack(){
quackBehavior.quack();
}
public void performFly(){
flyBehavior.fly();
}
//可以通过如duck.setFlyBehavior(new FlyWithWings())来设定飞行行为
public void setFlyBehavior(FlyBehavior fb){
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb){
quackBehavior = qb;
}
}
以下是其中一种红头鸭子的代码
public class MallardDuck extends Duck {
MallardDuck(){
flyBehavior = new FlyWithWings();//类变量flyBhehavior继承自Duck
quackBehavior = new Quack();//类变量quackBehavior继承自Duck
}
public void display(){
System.out.println("I am a MallardDuck");
}
}
总结
策划者模式定义了算法族,分别封装起来,让它们之间可以相互替换,该模式让算法的变化独立于客户
两个设计原则:
1、针对接口编程,而不是针对实现编程
2、多用组合,少用继承
组合
利用 “has a” 关系,在本例中,每个鸭子都有一个FlyBehavior和一个QuackBehavior,将飞行行为和叫声行为委托给它们俩完成。
当你将两个类结合起来使用,这就是 组合 ,这与继承不同的地方在于,鸭子的行为不是继承而来的,而是和适当的行为对象组合而来。