设计模式与行为型模式

设计模式与行为型模式

设计模式是在长时间实践中,开发人员总结出的优秀架构与解决方案。学习设计模式将有助于经验不足的开发人员在实际开发中,灵活地运用面向对象特性,并能够快速构建不同场景下的程序框架,写出优质代码。

设计模式分类

  • 行为型模式:关注对象行为功能上的抽象,从而提升对象在行为功能上的可拓展性,能以最少的代码变动完成功能的增减。
  • 结构型模式:关注对象之间结构关系上的抽象,从而提升对象结构的可维护性、代码的健壮性,能在结构层面上尽可能的解耦合。
  • 创建型模式

本章内容主要探讨设计模式中的行为型模式。

行为型模式分类

本章介绍三种行为型模式。

  • 模版方法模式
  • 策略模式
  • 迭代器模式

模版方法 Template Method

模版方法是一种针对接口编程的设计。

模版方法的思想是,基类是一个模板(也可以称作原型)。基类体现的是“抽象概念”,里面定义若干个纯虚函数,这些函数提供了这个类的“接口”。

比如,如果一个操作有 operation1operation2 两个步骤,我们可以定义一个基类:

class AbstractClass {
public:
    virtual void Operation1() = 0;
    virtual void Operation2() = 0;
};

这个操作可能有很多个版本。每个版本的实现细节不同。比如,AbstractClass 可能是“监视计算机节点的负载状态”的过程,Operation1 是“得到总内存”,Operation2 是“得到已占用内存”。而这两种操作在不同的环境下,比如 Win32 和 Win64 下可能是不同的。这些不同的细节则由子类负责实现。

在使用时,抽象类的算法骨架提供了大致方法,再由这个方法来根据需要调用具体类的实现细节。比如:

class ConcreteClassA: public AbstractClass {
    void Operation1() {
        cout << "Do Operation 1 of A" << endl;
    }
    void Operation2() {
        cout << "Do Operation 2 of A" << endl;
    }
};

这样当我们要拓展一种新的实现类时,重新对基类进行继承与实现即可,无需对已有的实现类进行修改。

由模板实现多态

现在我们有了很多继承自同一个抽象类的实现类,该怎么实现多态呢?

我们知道,模板多态是依赖于指针和引用的。编译器能够通过指针和引用判断实际指向的类型,并且调用实际类型里面 override 了的虚函数。所以在使用模板模式的时候,常常创建基类指针来调用实现类的函数。

例:

#include <iostream>
using namespace std;

class AbstractClass {
public:
    virtual void Operation1() = 0;
    virtual void Operation2() = 0;
};

class ConcreteClassA: public AbstractClass {
public:
    void Operation1() {
        cout << "Do Operation 1 of A" << endl;
    }
    void Operation2() {
        cout << "Do Operation 2 of A" << endl;
    }
};

class ConcreteClassB: public AbstractClass {
public:
    void Operation1() {
        cout << "Do Operation 1 of B" << endl;
    }
    void Operation2() {
        cout << "Do Operation 2 of B" << endl;
    }
};

int main() {
    AbstractClass *abstract;
    abstract = new ConcreteClassB();
    abstract->Operation1();
    abstract->Operation2();
    return 0;
}

策略模式 Strategy Method

模板模式中,每个实现类里面可能有多个功能(比如 Operation1Operation2 等)。可能有两个类,它们各有 NN 个功能,其中只有一个的实现有区别。在模板模式下,我们却必须实现两个类。这就很麻烦。

为了避免这种情况,使用策略模式。

策略模式:定义一系列算法并加以封装,使得这些算法可以互相替换。这样,一种算法就不需要依附于某个实现类了,而是自成一类。

具体而言

  • 每一种行为各自有方法虚基类 AB

  • 每一个方法基类 A 有若干具体的方法 A1A2A3​,每一种都会继承 A

  • 所有对象具有一个对象基类 O​,对象基类 O​ 含有所有的方法基类指针 A*​、B*​,从而实现多态。注意到对象基类如果仅含有方法基类指针,实际上没法调用方法基类的方法,故而还需要调用接口

  • 每种对象是一个具体的对象类 O1O2O3​,每种都会继承对象类 O​,同时让方法基类指针 A*​、B* ​具体指向方法派生类上 AxBy