1 会飞的鸭子
Duck 基类,包含两个成员函数 (swim, display);派生类 MallardDuck,RedheadDuck 和 RubberDuck,各自重写继承自基类的 display 成员函数
1 2 3 4 5 6 7 8 9 10 11 12 |
|
现在要求,为鸭子增加会飞的技能 -- fly,那么应该如何设计呢?
1.1 继承
考虑到并非所有的鸭子都会飞,可在 Duck 中加个普通虚函数 fly,则“会飞”的派生类继承 fly 的一个缺省实现,而“不会飞”的派生类重写 fly 的实现
1 2 3 |
|
1.2 接口
实际上,使用一般虚函数来实现多态并非良策,在前文 C++11 之 override 关键字中的 “1.2 一般虚函数” 已经有所解释,常用的代替方法是 “纯虚函数 + 缺省实现”,
即将 fly 在基类中声明为纯虚函数,同时写一个缺省实现
因为是纯虚函数,所以只有“接口”会被继承,而缺省的“实现”却不会被继承,是否调用基类里 fly 的缺省实现,则取决于派生类里重写的 fly 函数
1 2 |
|
1.3 设计模式
到目前为止,并没有使用设计模式,但问题看上去已经被解决了,实际上使用或不使用设计模式,取决于实际需求,也取决于开发者
<Design Patterns> 中,关于策略模式的适用情景,如下所示:
1)many related classes differ only in their behavior
2)you need different variants of an algorithm
3)an algorithm uses data that clients shouldn't know about
4)a class defines many behaviors, and these appear as multiple conditional statements in its operations
显然,鸭子的各个派生类属于 “related classes”,关键就在于“飞”这个行为,如果只是将“飞”的行为,简单划分为“会飞”和“不会飞”,则不使用设计模式完全可以
如果“飞行方式”,随着派生类的增多,至少会有几十种;或者视“飞行方式”为一种算法,以后还会不断改进;再或“飞行方式”作为封装算法,提供给第三方使用。
那么此时,设计模式的价值就体现出来了 -- 易复用,易扩展,易维护。
而第 4) 种适用情景,多见于重构之中 -- "Replace Type Code with State/Strategy"
2 设计原则
在引出策略模式之前,先来看面向对象的三个设计原则
1)隔离变化:identify what varies and separate them from what stays the same
Duck 基类中, 很明显“飞行方式“是变化的,于是把 fly 择出来,和剩余不变的分隔开来
2)编程到接口:program to an interface, not an implementation
分出 fly 之后,将其封装为一个接口,里面实现各种不同的“飞行方式” (一系列”算法“),添加或修改算法都在这个接口里面进行。“接口”对应于 C++ 便是抽象基类,
即将“飞行方式”封装为 FlyBehavior 类,该类中声明 fly 成员函数为纯虚函数
1 2 3 4 5 6 7 8 9 10 11 |
|
具体实现各种不同的算法 -- “飞行方式”,如下所示:
1 2 3 4 5 |
|
3)复合 > 继承:favor composition (has-a) over inheritance (is-a)
<Effective C++> 条款 32 中提到,公有继承即是“is-a”,而条款 38 则提及 Composition (复合或组合) 的一个含义是 “has-a”。因此,可以在 Duck 基类中,
声明 FlyBehavior 类型的指针,如此,只需通过指针 _pfB 便可调用相应的”算法“ -- ”飞行方式“
1 2 3 4 5 6 |
|
3 策略模式
3.1 内容
即便不懂设计模式,只有严格按照上面的三个设计原则,则最后的设计思路也会和策略模式类似,可能只是一些细微处的差别
下面来看策略模式的具体内容和结构图:
Defines a family ofalgorithms, encapsulateseach one, and makes theminterchangeable.Strategy lets the algorithm vary independently
from clients that use it.
Context指向Strategy (由指针实现);Context 通过Strategy 接口,调用一系列算法;ConcreteStrategy 则实现了一系列具体的算法
3.2 智能指针上例中,策略模式的“接口” 对应于抽象基类 FlyBehavior,“算法实现”分别对应派生类 FlyWithWings, FlyNoWay, FlyWithRocket,“引用”对应 _pfB 指针