Head First 设计模式学习 ——策略模式简单理解
问题来源:
在子类继承父类时,如果子类不需要使用父类中的某些方法,该如何处理?
举个例子,如下图所示,父类Duck类抽象了现实世界中的鸭子,拥有quack()(鸣叫)、swim()(游泳)和fly()(飞行)成员方法;子类WildDuck(野鸭)、WoodDuck(木头鸭)均继承Duck类。由于木头鸭子不会叫、也不会飞,因此WoodDuck继承Duck类的quack()方法和fly()方法是没有意义的。针对这一问题,有三种解决方法。
方法一:
覆盖WoodDuck类中的quack()和fly()方法,让这两个方法什么都不做,即:
public void quack() {
//此处什么都不做
}
public void fly() {
//此处什么都不做
}
这样就使WoodDuck类中的quack()方法和fly()方法不起作用,保证木头鸭子不能飞也不能叫。但是这种方法也存在一些问题,当有新的子类加入,使用者需要不断检查是否覆盖quack()方法和fly()方法,显然当新加入的子类数量庞大时,这种方法的工作量也会随之增长。
方法二:
将“飞行”和“鸣叫”抽取出来,形成两个接口,如下如所示:
本着“谁用接口谁实现的原则”,需要“飞行”的鸭子就实现“飞行”接口;需要“鸣叫”的鸭子就实现“鸣叫”接口;既需要“飞行”又需要“鸣叫”的鸭子就同时将两个接口实现;不需要“飞行”也不需要“鸣叫”的鸭子就不用实现这两个接口。
然而,这种方法的问题在于,每一个鸭子都需要重写quack()方法和fly()方法,使得重复的代码会变多,没有做到quack()方法和fly()方法的代码复用。举个例子:假设鸭子的飞行需要扇动翅膀、盘旋上升和保持平衡3个步骤。同时假设有红头鸭和绿头鸭两种鸭子,则红头鸭的fly()方法可以写为:
public fly() { //红头鸭
扇动翅膀();
盘旋上升();
保持平衡();
}
若此时新来一个子类绿头鸭,由于绿头鸭也会飞,所以绿头鸭的fly()方法也可以写为:
public fly() { //绿头鸭
扇动翅膀();
盘旋上升();
保持平衡();
}
观察上面两个fly()方法,可以发现绿头鸭的fly()方法和红头鸭的fly()方法存在着代码重复的问题。此外,飞行的行为可能不止一种,比如有的鸭子飞行不需要盘旋上升,而有的鸭子需要盘旋上升和扇动翅膀互相结合等等等等。这些问题需要子类根据实际情况重写fly()方法,因而存在代码重复和工作量大的问题。
方法三:
在方法二的基础上,构建两个类的集合,分别封装“飞行”行为和“鸣叫”行为,如下图所示:
所有的“飞行”行为都需要实现FlyBehavior接口,所有的“鸣叫”行为都需要实现QuackBehavior接口。对于WildDuck和WoodDuck来说,无需实现FlyBehavior和QuackBehavior,只需继承Duck类,并使用flyBehavior和quackBehavior实例化行为即可。
完整的代码如下:
FlyBehavior.java:
public interface FlyBehavior {
public void fly();
}
QuackBehavior.java:
public interface QuackBehavior {
public void quack();
}
Fly1.java:
public class Fly1 implements FlyBehavior {
public void fly() {
System.out.println("~~~I can fly~~~");
}
}
Fly2.java:
public class Fly2 implements FlyBehavior {
public void fly() {
System.out.println("~~~I can't fly~~~");
}
}
Quack1.java:
public class Quack1 implements QuackBehavior {
public void quack() {
System.out.println("~~~I can quack~~~");
}
}
Quack2.java:
public class Quack2 implements QuackBehavior {
public void quack() {
System.out.println("~~~I can't quack~~~");
}
}
Duck.java:
public class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {}
public void swim() {
System.out.println("~~~swim~~~");
}
public void performQuack() {
quackBehavior.quack();
}
public void performFly() {
flyBehavior.fly();
}
}
WildDuck.java:
public class WildDuck extends Duck {
public WildDuck() {
flyBehavior = new Fly1();
quackBehavior = new Quack1();
}
}
WoodDuck.java:
public class WoodDuck extends Duck {
public WoodDuck() {
flyBehavior = new Fly2();
quackBehavior = new Quack2();
}
}
test.java:
public class test {
public static void main(String[] args) {
//野鸭子
WildDuck wildDuck = new WildDuck();
wildDuck.performFly();
wildDuck.performQuack();
//木头鸭子
WoodDuck woodDuck = new WoodDuck();
woodDuck.performFly();
woodDuck.performQuack();
}
}
经过上面的实验后,给出《HeadFirst设计模式》一书对于策略模式的定义:策略模式定义了算法族,分别封装起来,让他们之间可以相互替换使用,此模式让算法的变换独立于使用算法的客户。在上面的实验中,飞行类封装了Fly1和Fly2两个类,鸣叫类封装了Quack1和Quack2两个类,WildDuck和WoodDuck(客户)可以在程序中指定使用Fly1还是Fly2,使用Quack1还是Quack2。使用这种模式可以很方便地添加新的行为如:Fly3、Fly4....。
参考自: