设计模式 策略模式

Posted 飞鸢逐浪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式 策略模式相关的知识,希望对你有一定的参考价值。

 

 一  入门

  摘自<Head First Design Patterns> chapter 1

1  飞翔的鸭子

  开发一款模拟鸭子的游戏。首先设计一个鸭子母类,里面有鸭子的叫声、游泳和外形三个成员函数,然后在野鸭和红头鸭两个子类中重写继承的外形函数。

技术分享

  更进一步,要求鸭子会飞,应该如何设计程序呢?

2  两种方法

1)  继承

  为 Duck 母类添加 fly() 成员函数,然后各个子类继承 fly()

   技术分享

  问题: 并不是所有的鸭子都会飞,比如“煮熟的鸭子”。解决: 不会飞的鸭子自母类继承 fly() 后,重写该函数。

  因为鸭子会不会飞,又发现一个bug,并不是所有的鸭子都会叫,比如“大黄鸭”。因此,不同的子类也需要重写 quack()

技术分享

  此时问题又来了,由于开发的是游戏软件,要求游戏每三个月更新一次,每次更新后鸭子的飞行方式和叫声都可能发生新的变化。那么,岂不是每三个月都要重写 RubberDuck 和 DecoyDuck 子类甚至更多子类的 fly() 和 quack() 函数,有点繁琐。

   于是,有了第二个方法...

2)  接口 (interface)

  视会飞和会叫为一种能力,并将 Flyable 和 Quackable 做成接口 (interface),然后在里面加上相应的函数 fly() 和 quack()

  这样,只有会飞的子类实现该接口 Flyable, 并且具有 fly() 函数。同理只有会叫的子类实现接口 Quackable 且有 quack() 函数。

技术分享

  问题又来了,飞行是鸭子的一种行为,不同的鸭子,其各自飞行的方式也不同。显然,每个子类中还得重写 fly() 和 quack()。因此,单纯的使用接口或继承都非最佳。

  那么,到底什么方法能完美解决鸭子 fly 和 quack 的问题呢?不着急,先看下面的三个设计原则。

3  三个原则

1)  identify what varies and separate them from what stays the same

   鸭子类中, 很明显“飞”和“叫”是变化的,于是先把 fly 和 quack 择出来,和其它不变的分隔开来。

  技术分享

2)  program to an interface, not an implementation

  技术分享

3)  favor composition over inheritance

    HAS-A better than IS-A

    Java: Duck 母类中定义 FlyBehavior 和 QuackBehavior 的成员对象 (FlyBehavior flyBehavior)

    C++: Duck 母类中定义 FlyBehavior 和 QuackBehavior 的指针 (FlyBehavior * pflyBehavior)

4  一个模式

    defines a family of algorithms,  encapsulates each one,  and makes them interchangeable.

    (Strategy lets the algorithm vary independently from clients that use it)

1)  client

  技术分享

2)  encapsulated fly behavior

  把飞行封装为接口,里面具体实现不同的飞行方式,即将各种”飞行方式“视为一系列”算法“,添加或者修改算法都在这个接口里面进行。

  这样,不同的鸭子子类,只需通过母类中的成员对象 (flyBehavior) 调用相应的”飞行算法“即可。

  技术分享

3)  encapsulated quack behavior

  Java: 直接使用关键字 interface 便可将 QuackBehavior 定义成接口

  C++: 接口 ≈ 抽象基类,将 QuackBehavior 定义为抽象基类,也即声明 quack() 为纯虚函数, 具体代码形式为 “void quack() = 0”

  技术分享

 二  进阶

  摘自<Design Paterns_Elements of Reusable Object-Oriented Software>

  上面偏重于入门,下面开始进阶,偏重于策略模式的适用情景。

1  Intent

  Define a family of algorithms, encapsulate each one, and make them interchangeable.

  Strategy lets the algorithm vary independently from clients that use it.

  技术分享

2  Applicability

1)  many related classes differ only in their behavior

  = strategies provide a way to configure a class with one of many behaviors

2)  you need different variants of an algorithm

  = strategies can be used when these variants are implemented as a class hierarchy of algorithms

3)  an algorithm uses data that clients should not know about

  = use strategies to avoid exposing complex, algorithm-specific data structures

4)  a class defines many behaviors, and these appear as multiple conditional statements in its operations

  = move related conditional branches into their own strategy class

3  Sample

  many algorithms exist for breaking a stream of text into lines, and hard-wiring such algorithms into the class that require them is not desirable.

技术分享

  C++ 实例:

1) class Composition

技术分享
/* Composition class maintains a collection of Component instances */
class Composition {
public:
    Composition(Compositor*);
void Repair();
private:
    Compositor* _compositor;
    Component* _components;    // the list of components
    int _componentCount;    // the number of components
    int _lineWidth;        // the Composition‘s line width
    int* _lineBreaks;    // the position of linebreaks in components
    int _lineCount;        // the number of lines
};

void Composition::Repair () {
    Coord* natural;
    Coord* stretchability;
    Coord* shrinkability;
    int componentCount;
    int* breaks;
    // prepare the arrays with the desired component sizes
    // ...
    // determine where the breaks are:
    int breakCount;
    breakCount = _compositor->Compose(
        natural, stretchability, shrinkability,
        componentCount, _lineWidth, breaks
    );
    // lay out components according to breaks
    // ...
}
View Code

2) class Compositor and its subclasses

技术分享
/* Compositor is an abstract class(also interface) */
class Compositor{
public:
    virtual int Compose(
        Coord natural[], Coord stretch[],Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    ) = 0;
protected:
    Compositor();
};

/* Three subclasses */
class SimpleCompositor : public Compositor {
public:
    SimpleCompositor();
    
    virtual int Compose(
        Coord natural[], Coord stretch[], Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    );
    // ...
};

class TeXCompositor : public Compositor {
public:
    TeXCompositor();
    virtual int Compose(
        Coord natural[], Coord stretch[], Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    );
    // ...
};

class ArrayCompositor : public Compositor{
public:
    ArrayCompositor(int interval);
    
    virtual int Compose(
        Coord natural[], Coord stretch[], Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    );
    // ...
};
View Code

3) instantiation

技术分享
Composition* quick = new Composition(new SimpleCompositor);
Composition* slick = new Composition(new TeXCompositor);
Composition* iconic = new Composition(new ArrayCompositor(100));
View Code

 

以上是关于设计模式 策略模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式入门-策略模式&观察者模式

设计模式 策略模式

入门设计模式之汇总篇

策略模式(从放弃到入门)

设计模式(十四)——策略模式

在C#中理解和实现策略模式的绝对入门教程