本文是对 Head First设计模式 一书中策略模式的一个总结。
策略模式:定义了算法族,分别封装起来,让它们可以互相替换,此模式让算法独立于使用算法的客户端。
接下来我们通过一个鸭子类(Duck)来分析此模式,先来看看这个Duck类的定义:
Duck是一个抽象类,MallardDuck(绿头鸭)和RedheadDuck(红头鸭)分别继承于Duck,假设所有鸭子都会叫(quack)和游泳(swim),因此在父类中实现了quack()和swim()这两个方法,但是每一种鸭子的外观是不相同的,因此display()在父类中定义为抽象方法,每个子类分别进行实现。
软件开发中有一个不变的真理:change,需求时刻都在改变。比如现在有个需求,要让鸭子飞起来,如何实现?第一个想到的办法就是在超类中增加一个fly()方法并实现,这样所有继承了Duck类的鸭子都可以飞起来了。
但现实情况是并非所有的鸭子都会飞,比如RubberDuck(橡皮鸭子)就不会飞,这样在父类中定义 fly()方法就不太合适了,不能强制让一个橡皮鸭子飞吧。这时会有人说那还不简单,在橡皮鸭子中将fly()方法进行覆盖,进行空实现,这种做法貌似可以解决这个问题,但是如果有更多类型的鸭子呢,这时你将不得不对每一种类型的鸭子进行考虑要不要覆盖fly()方法,而且万一某个飞行行为要改变你是不是得修改所有继承于Duck的子类中的fly()方法?
这时我们发现使用继承并不能很好的解决此问题,因为鸭子的行为在子类里不断地改变,并且让所有的子类都有这些行为是不恰当的。这时候我们的第一个设计原则该登场了:
设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
这个设计原则可以通俗的这样理解:“把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分”。这几乎是每个设计模式背后的精神所在。
对于我们的例子中,鸭子的飞行行为就是可变化之处,我们要做的就是分开鸭子中变化和不会变化的部分
fly()是变化的部分,我们将他分离出来(远离Duck),建立一个新类。这个新类如何设计呢?我们希望一切能有弹性,因此第二个设计原则登场了:
设计原则:针对接口编程,而不是针对实现编程。
我们定义一个飞行行为相关的接口:FlyBehavior, 来代表飞行行为,这样具体的飞行行为将由实现了FlyBehavior接口的类实现,这样就针对接口编程了。“针对接口编程”真正的意思是“针对超类型编程”,关键在于多态,可以更明确地说成“变量的类型声明应该是超类型”,通常是一个抽象类或接口。
Duck类:
public abstract class Duck {
FlyBehavior flyBehavior;
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void performFly(){
flyBehavior.fly();
}
public void quack(){
System.out.println("quack!");
}
public void swim(){
System.out.println("swim");
}
public abstract void display();
}
FlyBehavior类:
public interface FlyBehavior {
void fly();
}
FlyWithWings类:
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("I'm flying!!");
}
}
FlyNoWay类:
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("I can't fly");
}
}
MallardDuck类:
public class MallardDuck extends Duck {
public MallardDuck() {
this.flyBehavior = new FlyWithWings();
}
@Override
public void display() {
System.out.println("I'm MallardDuck!");
}
}
Main类:
public class Main {
public static void main(String[] args) {
MallardDuck mallardDuck = new MallardDuck();
mallardDuck.performFly();
// 动态改变鸭子的飞行行为
mallardDuck.setFlyBehavior(new FlyNoWay());
mallardDuck.performFly();
}
}