C++ 之 策略模式

1  会飞的鸭 

  Duck 基类,含成员函数 Swim() 和 Display();派生类
MallardDuck,RedheadDuck 和 RubberDuck,各自又写 Display()

class Duck 
{
public:
    void Swim();
    virtual void Display();
};

class MallardDuck : public Duck 
{
public:
    void Display(); // adding virtual is OK but not necessary
};

class RedheadDuck ...

class RubberDuck ...

  现在要求,为鸭子增加飞的技能 — Fly,应该怎么规划为?

1.1  继承

  考虑到不要有的鸭都见面奇怪,可于 Duck 中加普通虚函数
Fly(),则“会飞”的接续 Fly() ,“不见面奇怪”的重写 Fly()

void Duck::Fly() {  std::cout << "I am flying !" << std::endl;  }

void RubberDuck::Fly() {  std::cout << "I cannot fly !" << std::endl;  }

1.2  接口

  用寻常虚函数并非良策,C++11 之 override
关键字 “1.2 普通虚函数” 中已经说。代替方法是 “纯虚函数 + 缺省落实”,即将基类中的 Fly()
声明也纯虚函数,同时写一个缺省实现

 
因为是纯虚函数,所以只有“接口”会让接续,而不够省之“实现”却不见面于延续,是否调用
Fly() 的缺省贯彻,则取决于重写的 Fly()

void MallardDuck::Fly() { Duck::Fly(); } 

void RedheadDuck::Fly() { Duck::Fly(); }

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() 为纯虚函数

class FlyBehavior 
{
public:
    virtual void Fly() = 0;
};

class FlyWithWings : public FlyBehavior 
{
public:
    virtual void Fly();
};

class FlyNoWay ...

class FlyWithRocket ...

  具体实现各种不同的算法 —
“飞行方式”,如下:

void FlyWithWings::Fly() {  std::cout << "I am flying !" << std::endl;  }

void FlyNoWay::Fly() {  std::cout << "I cannot fly !" << std::endl;  }

void FlyWithRocket::Fly() {  std::cout << "I am flying with a rocket !" << std::endl; }

3)  复合 > 继承:favor
composition (has-a) over inheritance (is-a)

   国有继承即凡是 “is-a”,而 Composition (复合或结成) 的意思是
“has-a”,故,可每当 Duck 基类中,声明 FlyBehavior
型指针,如此,只待经过指针 _pfB 便可调用相应的”算法“ —
”飞行方式“

class Duck 
{
    ...
private:
    FlyBehavior* fb_;  // 或 std::unique_ptr<FlyBehavior> fb_;
};

 

3  策略模式

3.1  内容

  即便不明了设计模式,只要严格按遵守 隔离变化 –> 编程到接口 –> 复合 三单原则,则计划思路也会暨策略模式类似:

  下面是政策模式的具体内容:

  Defines a family of algorithms,
 encapsulates each one,  and makes
them interchangeable.  Strategy
lets the algorithm vary independently from clients that use it.

 
C++ 1

  Context 指向 Strategy
(由指针实现);Context 通过 Strategy
接口
,调用一系列算法;ConcreteStrategy
实现了一样多重实际的算法

3.2  智能指针

  上例被,策略模式之“接口” 对诺给 FlyBehavior
类,“算法实现”分别针对应派生类 FlyWithWings, FlyNoWay,
FlyWithRocket,“引用”对诺 fb_ 指针

  为了简化内存管理,可将 fb_
声明也一个“智能指针”,如此,则非待手动实现析构函数,采用编译器默认生成的即可。

Duck::Duck(FlyBehavior *fb)
    : fb_(fb)
{}

3.3  分析 

  直观上看, Duck 对应为 Context,实际上是那个派生类 MallardDuck 等,通过
FlyBehavior 接口来调用各种“飞行方法”。因此,需要以逐一派生类的构造函数中,初始化 fb_

MallardDuck::MallardDuck(FlyBehavior *fb)
    : Duck(fb)
{}

 然后,在 Duck 基类中,通过指针 fb_, 实现对 Fly() 的调用

void Duck::PerformFly()
{
    fb_->Fly();
}

  除了在构造函数中初始化 fb_ 外,还而每当 Duck 类中,定义一个
SetFlyBehavior 成员函数,动态的设置“飞行方法”

void Duck::SetFlyBehavior(FlyBehavior *fb)
{
    fb_ = fb;
}

3.4  main 函数  

  因为 main 执行完毕后,程序为就算终止了,所以对于简易程序,new
了指针后,可以绝不 delete

int main ()
{
    FlyBehavior *pfWings = new FlyWithWings;
    FlyBehavior *pfNo = new FlyNoWay;
    FlyBehavior *pfRocket = new FlyWithRocket;

    // fly with wings
    Duck *pDuck = new MallardDuck(pfWings);
    pDuck->PerformFly();

    // fly with a rocket
    pDuck->SetFlyBehavior(pfRocket);
    pDuck->PerformFly();
}

  

 代码链接: https://github.com/fengyibei/Strategy

 

小结

1)  面向对象的老三独规划规范:隔离变化,编程到接口,复合 > 继承

2)  策略模式要涉及的是“一名目繁多算法“,熟悉那个适用的季种植现象

 

参考资料

 <大话设计模式> 第二章

 <Head First Design Patterns> chapter 1

 <Effective C++> item 32, item 38

 <Design Patterns> Strategy

 <Refactoring> chapter 8

  Herb Sutter, GotW #91 Solution: Smart Pointer Parameters