接《HeadFirst设计模式-策略模式-鸭子类的常规实现》上文,当前场景下,推荐做法是使用组合。引入一个设计原则:
设计原则
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
这里会经常发生变化的是飞行和叫声行为,则将这两个行为抽出单独成类。
飞行行为flyBehavior,叫声行为quackBehavior。
那么,具体如何设计这两个行为呢。我们希望一切都能有弹性,毕竟,正是因为一开始的鸭子行为没有弹性,才让我们走上现在这条路。我们还想能够指定行为到鸭子的实例。比如,想要产生绿头鸭实例,并指定特性类型的飞行行为给它。干脆顺便让鸭子的行为可以动态地改变好了。话句话说,我们应该在鸭子类中包含设定行为的方法,就可以在运行时动态地改变绿头鸭的飞行行为。
为了实现这个目标,引入第二个设计原则:
设计原则
针对接口编程,而不是针对实现编程。
也就是这次鸭子类不会负责具体实现飞行接口和叫声接口,反而是由其他类专门实现飞行行为和叫声行为。
这样,实际的行为就不会被绑死在鸭子的子类中。
代码实例:
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("swim");
}
}
public class MallardDuck extends Duck {
public MallardDuck() {
// 具体行为实现
quackBehavior = new QuackNormal();
flyBehavior = new FlyWithWings();
}
}
具体实例的调用
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
}
动态设定行为
现在在鸭子里建立的动态功能还没有用到,我们可以在鸭子子类通过设定方法(setter method)来设定鸭子的行为,而不是在鸭子的构造器内实例化
在Duck类中,加入两个新方法:
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
从此以后,我们可以随时调用这两个方法改变鸭子的行为。
小结
通过本例子,可以继续引出一个设计原则
设计原则
多用组合,少用继承。
如你所见,使用组合建立系统具有很大的弹性,不仅可将算法封装成类,更可以在运行时动态地改变行为。
这就是策略模式,将会变化的算法分别封装起来,让它们之间可以互相替换,让算法的变化独立于使用算法的客户。