1 鸭子问题
现在有种类繁多的鸭子(如野鸭、北京鸭、玩具鸭等),鸭子有各种行为,如叫、游泳等,现在要求显示鸭子的信息。传统方案是通过设计一个Duck父类,在Duck中定义了鸭子的行为(叫、游泳),然后让野鸭、玩具鸭等都继承Duck,覆盖相应的方法,这样设计会导致某些类需要覆盖基类的所有方法。
2 策略模式介绍
在策略模式(Strategy Pattern)中定义了算法族,分别封装,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。这种算法体现了几个原则:
1)把变化的代码从不变的代码中分离出来
2)针对接口编程而不是具体的类(定义了策略接口)
3)多用聚合/组合,少用继承(客户端通过组合方式使用策略)

策略模式类图
3 代码实现
为了简单,这里用Swim这个策略接口为例来实现策略模式。
首先有策略接口Swim,描述是否会游泳:
public interface Swim {
void swim();
}
描述会游泳的策略实现类CanSwim:
public class CanSwim implements Swim {
@Override
public void swim() {
System.out.println("会游泳");
}
}
描述不会游泳的策略实现类CannotSwim:
public class CannotSwim implements Swim {
@Override
public void swim() {
System.out.println("不会游泳");
}
}
然后是鸭子的抽象基类Duck,对应到Context
public abstract class Duck {
Swim swimBehavior;
public abstract void display();
public void quack() {
System.out.println("鸭子在嘎嘎叫");
}
public void swim() {
if (swimBehavior != null) {
swimBehavior.swim();
}
}
}
野鸭会游泳,所以依赖CanSwim:
public class WildDuck extends Duck {
public WildDuck() {
swimBehavior = new CanSwim();
}
@Override
public void display() {
System.out.println("这是野鸭");
}
}
玩具鸭不会游泳,所以依赖CannotSwim:
public class ToyDuck extends Duck {
public ToyDuck() {
swimBehavior = new CannotSwim();
}
@Override
public void display() {
System.out.println("这是玩具鸭");
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
Duck wildDuck = new WildDuck();
wildDuck.display();
wildDuck.swim();
Duck toyDuck = new ToyDuck();
toyDuck.display();
toyDuck.swim();
}
}
输出结果:
这是野鸭
会游泳
这是玩具鸭
不会游泳

策略模式实现鸭子问题的类图
4 策略模式的特点
1)策略模式的关键点在于分析项目中变化与不变的部分。
2)策略模式的核心思想是:多用聚合/组合,少用继承,用行为类组合比用行为类继承更有弹性。
3)策略模式体现了“对修改关闭,对扩展开放”的原则,客户端增加行为不需要修改原代码,只需要添加一种策略(或行为)即可,避免了使用多重转移语句(if-else...else-if)。
4)策略模式将算法封装在独立的Strategy类中,使得它可以独立于Context而改变,易于切换、理解和扩展。
5)每增加一个策略就要增加一个类,当策略过多时会导致类数目庞大。