策略模式:定义算法族,封装起来,让算法独立于使用算法的类。
原则:
- 封装变化;
- 多用组合,少用继承;
- 针对接口编程,不针对实现编程。
设计一个模拟鸭子应用,如果不使用策略模式,大致结构是这样的:
红头鸭、绿头鸭、橡皮鸭、诱饵鸭有共同的父类,它们分别实现自己的 fly() 和 quack() 方法,比如红头呀能叫能飞,橡皮鸭能叫不能飞,诱饵鸭不能飞不能叫……
在这个设计下,每定义一个新的鸭子都要重写这个类下的 fly() 和 quack() 方法,这会导致大量实现 fly() 和 quack() 方法的重复代码,也就是说 fly() 和 quack() 这两个行为和“鸭子类”耦合在了一起。这些代码是容易变化的,要把它们从原来的类中分离出来重新封装(即 1. 封装变化),通过组合而不是继承来与“鸭子类”配合(即 2. 多用组合,少用继承。大多数情况下继承意味着耦合)。然后再根据具体的行为来实现代码(即 3. 针对接口编程)。
使用策略模式后的类图:
使用策略模式,将 Fly 和 Quack 行为分离出来,封装成一个接口,使用行为类实现分别实现不同的行为。在 Duck 中设置两个实例变量分别引用它们。这样,之后可以更好的应对这部分的变化。在子类化 Duck 时,也可以动态的指定子类的行为。并通过调用 perfrom 方法委托行为类来实现行为。
CPP 的实现:
Duck 类:
class Duck {
public:
Duck() = default;
~Duck();
FlyBehavior *flyBehavior;
QuackBehavior *quackBehavior;
void swim();
void display();
void perfromQuack();
void perfromFly();
void setFlyBehavior(FlyBehavior *flyBehavior);
void setQuackBehavior(QuackBehavior *quackBehavior);
};
两个行为接口,CPP 中没有 Java 中的 interface 的概念。使用抽象基类实现:
class FlyBehavior {
public:
virtual void fly() const = 0;
};
class QuackBehavior {
public:
virtual void quack() const = 0;
};
定义具体的行为类实现抽象行为基类的接口,Duck 的子类在各自的构造方法中指定自己的行为类。这样策略模式就实现了。