策略模式

定义

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

结构图

实现

实现鸭子的行为

考虑实现一个鸭子类,鸭子可能会有飞(Fly)和叫(Quack)的行为,所以先建立两个接口,分别代表飞的行为和叫的行为:

1
2
3
public interface FlyBehavior {
public void fly();
}
1
2
3
public interface QuackBehavior {
public void quack();
}

然后建立它们对应的类,负责实现具体的行为.

例如飞行的行为有用翅膀飞行,也可以是不会飞:

1
2
3
4
5
6
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("I'm flying");
}
}
1
2
3
4
5
6
public class FlyNoway implements FlyBehavior{
@Override
public void fly() {
System.out.println("Sorry,I can't fly.");
}
}

在叫的行为中,可能是呱呱叫,也可能是吱吱叫,也可能是不会叫:

1
2
3
4
5
6
public class Quack implements QuackBehavior{
@Override
public void quack() {
System.out.println("Quack!");
}
}
1
2
3
4
5
6
public class Squeak implements QuackBehavior{
@Override
public void quack() {
System.out.println("Squeak");
}
}
1
2
3
4
5
6
public class MuteQuack implements QuackBehavior{
@Override
public void quack() {
System.out.println("<<>Silence>");
}
}

此时,该类的结构图就是这样了:

这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子类无关了,这么一来,有了继承的“复用”好处,但是却没有继承所带来的包袱。而我们新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为的鸭子类。

整合鸭子的行为

关键在于,鸭子现在将会飞行和呱呱叫的动作“委托”别人处理,而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法。做法是这样的:

  1. 首先,声明一个Duck类,在Duck类中加入两个实例变量,分别为:flyBehavior与quackBehavior。声明为接口类型,每个鸭子对象都会动态的设置这些变量以在运行时引用正确的类型。
  2. 现在来实现performQuack()和performFly()这两个方法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public void performFly() {
    flyBehavior.fly();
    }

    public void performQuack() {
    quackBehavior.quack();
    }
    }

想进行呱呱叫的动作,Duck对象只要交quackBehavior对象去呱呱叫就行了,想要飞的行为,只要叫flyBehavior对象
去飞就行了。我们不在乎接口的对象到底是什么,我们只关心该对象知道如何进行呱呱叫就够了。

动态行为设定

假设我们想在鸭子子类中通过“设定方法”来设定鸭子的行为,而不是在鸭子的构造期内实例化,所以:

  1. 在Duck类中,加入两个新方法,和一些其他的行为方法:
    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
    public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
    this.quackBehavior = quackBehavior;
    }

    public abstract void display();

    public void performFly() {
    flyBehavior.fly();
    }

    public void performQuack() {
    quackBehavior.quack();
    }

    public void swim() {
    System.out.println("All ducks float,even decoys!");
    }
    }

从此以后,我们可以“随时”调用setFlyBehavior()和setQuackBehavior()这两个方法改变鸭子的行为。

查看结果

创造一个新的鸭子类型:模型鸭,其中一开始我们的鸭子是不会飞也不会叫的

1
2
3
4
5
6
7
8
9
10
11
12
public class ModelDuck extends Duck {

public ModelDuck() {
flyBehavior = new FlyNoway();
quackBehavior = new Quack();
}

@Override
public void display() {
System.out.println("I'm a model duck");
}
}

但是在运行中,我们如果想要改变它的行为,让它变成会飞且会呱呱叫的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
Duck model = new ModelDuck();
model.performFly();
model.performQuack();
model.display();

System.out.println("替换策略后.......");

model.setFlyBehavior(new FlyWithWings());
model.setQuackBehavior(new Squeak());
model.performFly();
model.performQuack();
model.display();
}
}

输出的结果为:

1
2
3
4
5
6
7
Sorry,I can't fly.
<<Silence>>
I'm a model duck
替换策略后.......
I'm flying
Squeak
I'm a model duck

整体结构

此时整体的格局如下:

总结

我们把描述事情的方式稍有改变,不再把鸭子的行为说成是“一组行为”,我们开始把行为想成是“一族算法”,现在回到策略模式的定义上面再看就豁然开朗:

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。