前言
对白很有趣,业务情景营造地很有氛围,如果还不会设计模式的话是值得一读的。
本笔记当然不会有那些有趣的图片和氛围,内容也会尽量浓缩。
策略模式(Strategy)
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
栗子
某公司做了一款鸭子游戏,游戏中的鸭子可以游泳,可以呱呱叫。系统设计了一个鸭子抽象类,让各种鸭子都继承它:
1 2 3 4 5 6 7 8 9 10 11
| abstract class Duck { void quack() { System.out.println("呱呱叫"); }
void swim() { System.out.println("游泳"); }
abstract void display(); }
|
其中一些鸭子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MallardDuck extends Duck { @Override void display() { System.out.println("绿头鸭子"); } }
class RedheadDuck extends Duck { @Override void display() { System.out.println("红头鸭子"); } }
|
提需求
现在公司要游戏中的鸭子能飞。
最简单的方案:给鸭子抽象类加上 fly 方法,这样鸭子们都能飞了。
1 2 3
| void fly() { System.out.println("飞"); }
|
对于真鸭子来说,没多大问题。但对于假鸭子来说,就不对了,比如橡皮鸭,根本就不能飞,而且另一个 quack 方法也不适用于橡皮鸭,因为它是吱吱叫的。
当然,对于橡皮鸭,你可以覆盖 quack 方法和 fly 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class RubberDuck extends Duck { @Override void quack() { System.out.println("吱吱叫"); }
@Override void display() { System.out.println("橡皮鸭"); }
@Override void fly() { } }
|
但如果现在又新添加了一只诱饵鸭呢?它是木头假鸭,既不会飞又不会叫,如果还是使用上面的方法的话就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class DecoyDuck extends Duck { @Override void quack() { }
@Override void display() { System.out.println("诱饵鸭"); }
@Override void fly() { } }
|
给程序员很明显的感觉就是,并不需要这些什么都不做的行为,但不覆盖又不行,不然这些鸭子的行为就不对了。所以利用继承来提供鸭子的行为有以下缺点:
- 代码在多个子类中重复。
- 运行时的行为不容易改变。
- 很难知道所有鸭子的全部行为。
- 改变会牵一发动全身,造成其他鸭子不想要的改变。
满足需求
设计原则一:找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起。
既然鸭子的行为是变化的,那么根据原则一,我们要做的就是把鸭子的行为从鸭子抽象类 Duck 中抽出来,那抽出来后怎么定义具体鸭子的行为呢?那当然是定义具体鸭子的同时,定义其行为。
设计原则二:针对接口编程,而不是针对实现编程。
设计原则三:多用组合,少用继承。
根据原则二,写出对应的飞和叫这两种行为的接口:
飞的行为:
1 2 3
| interface FlayBehavior { void fly(); }
|
具体飞的动作的两个实现:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class FlyWithWings implements FlayBehavior { @Override public void fly() { System.out.println("飞"); } }
class FlyNoWay implements FlayBehavior { @Override public void fly() { } }
|
叫的行为:
1 2 3
| interface QuackBehavior { void quack(); }
|
具体叫的动作的三个实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Quack implements QuackBehavior { @Override public void quack() { System.out.println("呱呱叫"); } }
class Squeak implements QuackBehavior { @Override public void quack() { System.out.println("橡皮鸭吱吱叫"); } }
class MuteQuack implements QuackBehavior { @Override public void quack() { } }
|
给鸭子抽象类添加这些行为,并且加入 setter,让这些行为可以动态设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| abstract class Duck { FlyBehavior flyBehavior; QuackBehavior quackBehavior;
void setFlyBehavior(FlyBehavior behavior) { this.flyBehavior = behavior; }
void setQuackBehavior(QuackBehavior behavior) { this.quackBehavior = behavior; }
void quack() { quackBehavior.quack(); }
void fly() { flyBehavior.fly(); }
void swim() { System.out.println("游泳"); }
abstract void display(); }
|
然后就可以在构造器里实现具体行为了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class MallardDuck extends Duck { public MallardDuck() { quackBehavior = new Quack(); flyBehavior = new FlyWithWings(); }
@Override void display() { System.out.println("绿头鸭子"); } }
class RubberDuck extends Duck { public RubberDuck() { quackBehavior = new Squeak(); flyBehavior = new FlyNoWay(); }
@Override void display() { System.out.println("橡皮鸭子"); } }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Main { public static void main(String[] args) throws IOException { RubberDuck rubberDuck = new RubberDuck(); rubberDuck.display(); rubberDuck.fly(); rubberDuck.quack(); QuackBehavior quack = new Quack(); rubberDuck.setQuackBehavior(quack); rubberDuck.quack(); System.out.println("--------"); MallardDuck mallardDuck = new MallardDuck(); mallardDuck.display(); mallardDuck.fly(); mallardDuck.quack(); } }
|
输出:
橡皮鸭子
橡皮鸭吱吱叫
呱呱叫
——–
绿头鸭子
飞
呱呱叫