策略模式
场景
设计鸭子模拟器系统,实现具有各种行为组合的鸭子
- 刚开始设计时,此系统设计了标准的OO技术,设计了一个鸭子超类,并让各种鸭子继承此超类
问题引入 : "让鸭子会飞! 此程序需要会飞的鸭子(火箭喷射鸭)" "鸭子的叫声不同(橡皮鸭, 模型鸭)"
较差的实现方式
-
在超类中增加fly()方法,并给予实现.
- 所有的鸭子都将拥有飞行的能力,但是并不是所有的鸭子都能飞(橡皮鸭)
- 解决: 让不会飞的鸭子重写fly方法,什么也不做
- 思考: 如果系统后续有各种各样的行为要增加(假设有50个), 并且每种行为有多种实现,并且一个鸭子可能有各种行为的组合 , 那么不具备这些能力的鸭子都要进行重写,这是一个好的设计吗?
-
将飞行行为定义成接口,让鸭子子类去实现
- 假设现在有100种鸭子,定义一个飞行的接口, 需要让有飞行能力的鸭子全部进行重写, 这样一来代码会重复很多,这是一个差劲的设计.
如何解决
- 问题出现在哪里?
继承的问题:对类的局部改动,尤其超类的局部改动,会影响所有子类部分。影响会有溢出效果超类挖的一个坑,每个子类都要来填,增加工作量,复杂度O(N^2) (增加N个行为,每个行为N个类来改)。不是好的设计方式
-
如何设计
-
分离可变部分和不可变部分
- 不经常变动部分: Duck类
- 可变部分: 可能新增的各种行为 fly() , quack()
- 这次鸭子类不会负责实现Flying和Quacking接口,反而是由我们
制造一组其他类专门实现FlyingBehavior和QuackBehavior接口, 这称为"行为类"
, 由行为类而不是Duck类来实现行为接口 - 关键在于,鸭子现在会将飞行和呱呱叫的动作"委托"(delegate) 别人代理,而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法
-
- 类图设计
-
重构后的分析
- 鸭子需要什么行为组合都能自行决定
- 可以在运行中动态改变自己的行为( setBehavior )
策略模式总结
定义:策略模式定义了算法族(行为族)
,分别封装起来,让它们之间可以相互替换,此模式让算法的变化部分 (鸭子行为) 独立于算法的客户(鸭子)
-
模式的理解
-
角色
- 拥有行为的主体(鸭子)
- 各种不同的行为,每种行为都有各种实现(飞行 / 叫声)
-
细节
- 主体使用
组合行为
的方式, 让主体任意的组合需要的行为 - 可以在运行中动态改变自己的行为( setBehavior )
- 主体使用
-
核心代码部分
- 鸭子类
public abstract class Duck {
FlyBehavior flyBehavior; //飞行行为
QuackBehavior quackBehavior; //叫声行为
public Duck() {
}
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
-
行为接口 (飞行叫声)
public interface FlyBehavior { public void fly(); } public interface QuackBehavior { public void quack(); }
-
实现行为接口
//不会飞行 public class FlyNoWay implements FlyBehavior { public void fly() { System.out.println("I can't fly"); } } //呱呱叫 public class Quack implements QuackBehavior { public void quack() { System.out.println("Quack"); } }
-
具体的鸭子
//模型鸭, 不会飞,只会呱呱叫 public class ModelDuck extends Duck { public ModelDuck() { flyBehavior = new FlyNoWay(); quackBehavior = new Quack(); } public void display() { System.out.println("I'm a model duck"); } }
-
鸭子模拟器(主程序)
public class MiniDuckSimulator { public static void main(String[] args) { MallardDuck mallard = new MallardDuck(); RubberDuck rubberDuckie = new RubberDuck(); DecoyDuck decoy = new DecoyDuck(); mallard.performQuack(); rubberDuckie.performQuack(); decoy.performQuack(); Duck model = new ModelDuck(); model.performFly(); //运行中改变行为 model.setFlyBehavior(new FlyRocketPowered()); model.performFly(); } }
-
输出结果
Quack Squeak << Silence >> I can't fly I'm flying with a rocket
参考
书籍: HeadFirst设计模式
代码参考地址: 我就是那个地址
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。