C++设计模式学习与样例代码实现

23种设计模式

设计模式是软件开发中用于解决常见问题的可复用解决方案。它们不仅提高了代码的可读性和可维护性,还能将常见的设计问题隔离程序的业务逻辑,重用已有的实现,消除重复代码,并消除手动调整代码以达到正确逻辑的所有痛苦。

常用设计模式分类
C++中常用的设计模式包括单例模式、工厂模式、抽象工厂模式、适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式、观察者模式和命令模式等。

单例模式

作用:保证一个类只有一个实例,并提供一个访问它的全局访问点,使得系统中只有唯一的一个对象实例。

应用:常用于管理资源,如日志、线程池。

实现要点:

需防止在外部调用类的构造函数而构造实例。
将构造函数的访问权限标记为private。
提供一个全局访问点,通过定义一个static函数返回唯一构造的实例。
拷贝构造和拷贝赋值符声明为private,或使用=delete来阻止拷贝。

在多线程环境下,普通的单例模式实现可能会出现多个实例被创建的问题,因此需要保证单例模式的线程安全性。以下为几种实现线程安全单例模式的方法及对应的 C++ 代码。

1. 饿汉式单例模式

饿汉式单例模式在程序启动时就创建单例实例,由于实例的创建发生在多线程启动之前,所以天然是线程安全的。

#include <iostream>

class Singleton {
private:
    // 私有构造函数,防止外部实例化
    Singleton() {}
    // 禁止拷贝构造函数
    Singleton(const Singleton&) = delete;
    // 禁止赋值运算符
    Singleton& operator=(const Singleton&) = delete;

    // 静态成员变量,在程序启动时就创建实例
    static Singleton instance;

public:
    // 静态方法,用于获取唯一实例
    static Singleton& getInstance() {
        return instance;
    }

    void showMessage() {
        std::cout << "This is a singleton instance." << std::endl;
    }
};

// 静态成员变量初始化
Singleton Singleton::instance;

int main() {
    Singleton& singleton1 = Singleton::getInstance();
    Singleton& singleton2 = Singleton::getInstance();

    if (&singleton1 == &singleton2) {
        std::cout << "Both references point to the same instance." << std::endl;
    }

    singleton1.showMessage();

    return 0;
}

解释:在上述代码中,Singleton 类的静态成员变量 instance 在程序启动时就被创建,之后通过 getInstance 方法返回该实例的引用。由于实例的创建发生在多线程启动之前,所以不会出现多线程竞争创建实例的问题,从而保证了线程安全。

2. 懒汉式单例模式(使用互斥锁)

懒汉式单例模式在第一次使用时才创建实例,为了保证线程安全,可以使用互斥锁来保护实例的创建过程。

#include <iostream>
#include <mutex>

class Singleton {
private:
    // 私有构造函数,防止外部实例化
    Singleton() {}
    // 禁止拷贝构造函数
    Singleton(const Singleton&) = delete;
    // 禁止赋值运算符
    Singleton& operator=(const Singleton&) = delete;

    // 静态成员变量,保存唯一实例
    static Singleton* instance;
    // 静态互斥锁,用于保护实例的创建过程
    static std::mutex mtx;

public:
    // 静态方法,用于获取唯一实例
    static Singleton* getInstance() {
        std::lock_guard<std::mutex> lock(mtx);
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    void showMessage() {
        std::cout << "This is a singleton instance." << std::endl;
    }
};

// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

int main() {
    Singleton* singleton1 = Singleton::getInstance();
    Singleton* singleton2 = Singleton::getInstance();

    if (singleton1 == singleton2) {
        std::cout << "Both pointers point to the same instance." << std::endl;
    }

    singleton1->showMessage();

    return 0;
}

解释:在 getInstance 方法中,使用 std::lock_guard 来自动管理互斥锁 mtx 的加锁和解锁操作。当多个线程同时调用 getInstance 方法时,只有一个线程能够获得锁并进入临界区,从而保证了实例只被创建一次。

3. 懒汉式单例模式(双重检查锁定)

双重检查锁定是对上述懒汉式单例模式的优化,减少了不必要的锁竞争。

#include <iostream>
#include <mutex>

class Singleton {
private:
    // 私有构造函数,防止外部实例化
    Singleton() {}
    // 禁止拷贝构造函数
    Singleton(const Singleton&) = delete;
    // 禁止赋值运算符
    Singleton& operator=(const Singleton&) = delete;

    // 静态成员变量,保存唯一实例
    static Singleton* instance;
    // 静态互斥锁,用于保护实例的创建过程
    static std::mutex mtx;

public:
    // 静态方法,用于获取唯一实例
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {
                instance = new Singleton();
            }
        }
        return instance;
    }

    void showMessage() {
        std::cout << "This is a singleton instance." << std::endl;
    }
};

// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

int main() {
    Singleton* singleton1 = Singleton::getInstance();
    Singleton* singleton2 = Singleton::getInstance();

    if (singleton1 == singleton2) {
        std::cout << "Both pointers point to the same instance." << std::endl;
    }

    singleton1->showMessage();

    return 0;
}

解释:在 getInstance 方法中,首先检查实例是否已经创建,如果没有创建,则加锁再次检查。这样可以避免每次调用 getInstance 方法都加锁,提高了性能。

4. C++11 及以上的局部静态变量方式

C++11 及以上标准保证了局部静态变量的初始化是线程安全的。

#include <iostream>

class Singleton {
private:
    // 私有构造函数,防止外部实例化
    Singleton() {}
    // 禁止拷贝构造函数
    Singleton(const Singleton&) = delete;
    // 禁止赋值运算符
    Singleton& operator=(const Singleton&) = delete;

public:
    // 静态方法,用于获取唯一实例
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }

    void showMessage() {
        std::cout << "This is a singleton instance." << std::endl;
    }
};

int main() {
    Singleton& singleton1 = Singleton::getInstance();
    Singleton& singleton2 = Singleton::getInstance();

    if (&singleton1 == &singleton2) {
        std::cout << "Both references point to the same instance." << std::endl;
    }

    singleton1.showMessage();

    return 0;
}

解释:在 getInstance 方法中,使用局部静态变量 instance 来保存单例实例。C++11 及以上标准保证了局部静态变量的初始化是线程安全的,即多个线程同时调用 getInstance 方法时,只会有一个线程对 instance 进行初始化,其他线程会等待初始化完成。这种方式简洁且高效,是推荐的实现方式。

工厂模式

工厂模式包括三种:简单工厂模式、工厂方法模式和抽象工厂模式。

简单工厂模式

简单工厂模式是工厂模式的基础版本,它通过一个工厂类根据传入的参数来创建不同类型的产品。

#include <iostream>
#include <string>

// 抽象产品类:定义产品的通用接口
class Product {
public:
    // 纯虚函数,具体产品类需实现该方法
    virtual void use() = 0;
    // 虚析构函数,确保正确释放派生类对象
    virtual ~Product() {}
};

// 具体产品类 A
class ConcreteProductA : public Product {
public:
    // 实现抽象产品类的 use 方法
    void use() override {
        std::cout << "Using ConcreteProductA" << std::endl;
    }
};

// 具体产品类 B
class ConcreteProductB : public Product {
public:
    // 实现抽象产品类的 use 方法
    void use() override {
        std::cout << "Using ConcreteProductB" << std::endl;
    }
};

// 简单工厂类:负责根据传入的类型创建具体产品
class SimpleFactory {
public:
    // 静态方法,根据产品类型创建产品对象
    static Product* createProduct(const std::string& type) {
        if (type == "A") {
            return new ConcreteProductA();
        } else if (type == "B") {
            return new ConcreteProductB();
        }
        return nullptr;
    }
};

int main() {
    // 使用简单工厂创建产品 A
    Product* productA = SimpleFactory::createProduct("A");
    if (productA) {
        productA->use();
        delete productA;
    }

    // 使用简单工厂创建产品 B
    Product* productB = SimpleFactory::createProduct("B");
    if (productB) {
        productB->use();
        delete productB;
    }

    return 0;
}

代码解释

  • Product 是抽象产品类,定义了产品的通用接口 use()
  • ConcreteProductAConcreteProductB 是具体产品类,实现了 use() 方法。
  • SimpleFactory 是简单工厂类,通过 createProduct 方法根据传入的类型创建具体产品。

工厂方法模式

工厂方法模式将产品的创建延迟到子类中实现,使得系统更易于扩展。

#include <iostream>

// 抽象产品类:定义产品的通用接口
class Product {
public:
    // 纯虚函数,具体产品类需实现该方法
    virtual void use() = 0;
    // 虚析构函数,确保正确释放派生类对象
    virtual ~Product() {}
};

// 具体产品类 A
class ConcreteProductA : public Product {
public:
    // 实现抽象产品类的 use 方法
    void use() override {
        std::cout << "Using ConcreteProductA" << std::endl;
    }
};

// 具体产品类 B
class ConcreteProductB : public Product {
public:
    // 实现抽象产品类的 use 方法
    void use() override {
        std::cout << "Using ConcreteProductB" << std::endl;
    }
};

// 抽象工厂类:定义创建产品的接口
class Factory {
public:
    // 纯虚函数,具体工厂类需实现该方法
    virtual Product* createProduct() = 0;
    // 虚析构函数,确保正确释放派生类对象
    virtual ~Factory() {}
};

// 具体工厂类 A:用于创建产品 A
class ConcreteFactoryA : public Factory {
public:
    // 实现抽象工厂类的 createProduct 方法
    Product* createProduct() override {
        return new ConcreteProductA();
    }
};

// 具体工厂类 B:用于创建产品 B
class ConcreteFactoryB : public Factory {
public:
    // 实现抽象工厂类的 createProduct 方法
    Product* createProduct() override {
        return new ConcreteProductB();
    }
};

int main() {
    // 创建具体工厂 A
    Factory* factoryA = new ConcreteFactoryA();
    // 使用工厂 A 创建产品 A
    Product* productA = factoryA->createProduct();
    if (productA) {
        productA->use();
        delete productA;
    }
    delete factoryA;

    // 创建具体工厂 B
    Factory* factoryB = new ConcreteFactoryB();
    // 使用工厂 B 创建产品 B
    Product* productB = factoryB->createProduct();
    if (productB) {
        productB->use();
        delete productB;
    }
    delete factoryB;

    return 0;
}

代码解释

  • Product 是抽象产品类,定义了产品的通用接口 use()
  • ConcreteProductAConcreteProductB 是具体产品类,实现了 use() 方法。
  • Factory 是抽象工厂类,定义了创建产品的接口 createProduct()
  • ConcreteFactoryAConcreteFactoryB 是具体工厂类,分别实现了 createProduct() 方法来创建不同的产品。

抽象工厂模式

抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

#include <iostream>

// 抽象产品类 A
class AbstractProductA {
public:
    // 纯虚函数,具体产品类需实现该方法
    virtual void useA() = 0;
    // 虚析构函数,确保正确释放派生类对象
    virtual ~AbstractProductA() {}
};

// 具体产品类 A1
class ConcreteProductA1 : public AbstractProductA {
public:
    // 实现抽象产品类 A 的 useA 方法
    void useA() override {
        std::cout << "Using ConcreteProductA1" << std::endl;
    }
};

// 具体产品类 A2
class ConcreteProductA2 : public AbstractProductA {
public:
    // 实现抽象产品类 A 的 useA 方法
    void useA() override {
        std::cout << "Using ConcreteProductA2" << std::endl;
    }
};

// 抽象产品类 B
class AbstractProductB {
public:
    // 纯虚函数,具体产品类需实现该方法
    virtual void useB() = 0;
    // 虚析构函数,确保正确释放派生类对象
    virtual ~AbstractProductB() {}
};

// 具体产品类 B1
class ConcreteProductB1 : public AbstractProductB {
public:
    // 实现抽象产品类 B 的 useB 方法
    void useB() override {
        std::cout << "Using ConcreteProductB1" << std::endl;
    }
};

// 具体产品类 B2
class ConcreteProductB2 : public AbstractProductB {
public:
    // 实现抽象产品类 B 的 useB 方法
    void useB() override {
        std::cout << "Using ConcreteProductB2" << std::endl;
    }
};

// 抽象工厂类:定义创建一系列产品的接口
class AbstractFactory {
public:
    // 纯虚函数,具体工厂类需实现该方法来创建产品 A
    virtual AbstractProductA* createProductA() = 0;
    // 纯虚函数,具体工厂类需实现该方法来创建产品 B
    virtual AbstractProductB* createProductB() = 0;
    // 虚析构函数,确保正确释放派生类对象
    virtual ~AbstractFactory() {}
};

// 具体工厂类 1:用于创建产品 A1 和产品 B1
class ConcreteFactory1 : public AbstractFactory {
public:
    // 实现抽象工厂类的 createProductA 方法
    AbstractProductA* createProductA() override {
        return new ConcreteProductA1();
    }
    // 实现抽象工厂类的 createProductB 方法
    AbstractProductB* createProductB() override {
        return new ConcreteProductB1();
    }
};

// 具体工厂类 2:用于创建产品 A2 和产品 B2
class ConcreteFactory2 : public AbstractFactory {
public:
    // 实现抽象工厂类的 createProductA 方法
    AbstractProductA* createProductA() override {
        return new ConcreteProductA2();
    }
    // 实现抽象工厂类的 createProductB 方法
    AbstractProductB* createProductB() override {
        return new ConcreteProductB2();
    }
};

int main() {
    // 创建具体工厂 1
    AbstractFactory* factory1 = new ConcreteFactory1();
    // 使用工厂 1 创建产品 A1
    AbstractProductA* productA1 = factory1->createProductA();
    if (productA1) {
        productA1->useA();
        delete productA1;
    }
    // 使用工厂 1 创建产品 B1
    AbstractProductB* productB1 = factory1->createProductB();
    if (productB1) {
        productB1->useB();
        delete productB1;
    }
    delete factory1;

    // 创建具体工厂 2
    AbstractFactory* factory2 = new ConcreteFactory2();
    // 使用工厂 2 创建产品 A2
    AbstractProductA* productA2 = factory2->createProductA();
    if (productA2) {
        productA2->useA();
        delete productA2;
    }
    // 使用工厂 2 创建产品 B2
    AbstractProductB* productB2 = factory2->createProductB();
    if (productB2) {
        productB2->useB();
        delete productB2;
    }
    delete factory2;

    return 0;
}

代码解释

  • AbstractProductAAbstractProductB 是抽象产品类,分别定义了产品 A 和产品 B 的通用接口。
  • ConcreteProductA1ConcreteProductA2ConcreteProductB1ConcreteProductB2 是具体产品类,实现了相应的抽象产品类的方法。
  • AbstractFactory 是抽象工厂类,定义了创建一系列产品的接口。
  • ConcreteFactory1ConcreteFactory2 是具体工厂类,分别实现了抽象工厂类的方法来创建不同组合的产品。

策略模式

定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。

应用:例如,设计一种空调,支持多种模式(冷风、热风、无风),可以使用策略模式来实现这些模式的动态切换,从而避免使用多重条件转移语句,使代码更易于维护。

#include <iostream>

// 抽象策略类:定义空调模式的接口
class AirConditionerMode {
public:
    /**
     * @brief 纯虚函数,具体模式类需要实现该方法来执行相应操作
     */
    virtual void operate() = 0;

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~AirConditionerMode() {}
};

// 具体策略类:冷风模式
class ColdWindMode : public AirConditionerMode {
public:
    /**
     * @brief 实现抽象策略类的 operate 方法,输出冷风模式的操作信息
     */
    void operate() override {
        std::cout << "Air conditioner is working in cold wind mode." << std::endl;
    }
};

// 具体策略类:热风模式
class HotWindMode : public AirConditionerMode {
public:
    /**
     * @brief 实现抽象策略类的 operate 方法,输出热风模式的操作信息
     */
    void operate() override {
        std::cout << "Air conditioner is working in hot wind mode." << std::endl;
    }
};

// 具体策略类:无风模式
class NoWindMode : public AirConditionerMode {
public:
    /**
     * @brief 实现抽象策略类的 operate 方法,输出无风模式的操作信息
     */
    void operate() override {
        std::cout << "Air conditioner is working in no wind mode." << std::endl;
    }
};

// 上下文类:管理空调模式的切换
class AirConditioner {
private:
    AirConditionerMode* currentMode;  // 当前使用的空调模式

public:
    /**
     * @brief 构造函数,初始化空调的初始模式
     * @param mode 指向具体空调模式的指针
     */
    AirConditioner(AirConditionerMode* mode) : currentMode(mode) {}

    /**
     * @brief 析构函数,释放当前模式对象的内存
     */
    ~AirConditioner() {
        delete currentMode;
    }

    /**
     * @brief 设置空调的新模式
     * @param mode 指向新的具体空调模式的指针
     */
    void setMode(AirConditionerMode* mode) {
        delete currentMode;  // 释放旧模式的内存
        currentMode = mode;  // 更新当前模式
    }

    /**
     * @brief 调用当前模式的操作方法
     */
    void operate() {
        currentMode->operate();
    }
};

int main() {
    // 创建冷风模式对象
    AirConditionerMode* coldMode = new ColdWindMode();
    // 创建空调对象,初始使用冷风模式
    AirConditioner ac(coldMode);
    // 执行当前模式的操作
    ac.operate();

    // 创建热风模式对象
    AirConditionerMode* hotMode = new HotWindMode();
    // 切换空调模式为热风模式
    ac.setMode(hotMode);
    // 执行当前模式的操作
    ac.operate();

    // 创建无风模式对象
    AirConditionerMode* noWindMode = new NoWindMode();
    // 切换空调模式为无风模式
    ac.setMode(noWindMode);
    // 执行当前模式的操作
    ac.operate();

    return 0;
}

代码解释

  1. 抽象策略类 AirConditionerMode:定义了一个纯虚函数 operate(),具体的空调模式类需要实现这个方法来执行相应的操作。同时,提供了一个虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
  2. 具体策略类(ColdWindModeHotWindModeNoWindMode:继承自抽象策略类 AirConditionerMode,并实现了 operate() 方法,分别输出不同模式下空调的操作信息。
  3. 上下文类 AirConditioner
    • 包含一个指向 AirConditionerMode 类型的指针 currentMode,用于存储当前使用的空调模式。
    • 构造函数用于初始化空调的初始模式。
    • 析构函数负责释放当前模式对象的内存。
    • setMode() 方法用于切换空调的模式,在切换时会先释放旧模式的内存,再更新当前模式。
    • operate() 方法调用当前模式的 operate() 方法来执行相应的操作。
  4. 主函数 main()
    • 创建不同的空调模式对象。
    • 创建空调对象,并初始化为冷风模式。
    • 调用 operate() 方法执行当前模式的操作。
    • 切换空调模式为热风模式和无风模式,并分别执行相应的操作。

通过策略模式,我们可以方便地实现空调模式的动态切换,避免了使用多重条件转移语句,使代码更易于维护和扩展。如果需要添加新的空调模式,只需创建一个新的具体策略类并实现 operate() 方法,然后在需要时切换到该模式即可。

适配器模式

作用:允许现有的类在不修改其内部结构的情况下在其他类中可用。它使用适配器来将不兼容的接口转换成用于目标类的接口。

应用:使客户端能够调用他们正在使用的接口,而实际上正在使用另一个接口,这个新接口已经与客户端的要求匹配。

类适配器模式

类适配器模式通过多重继承的方式,将适配者类的接口转换为目标接口。

#include <iostream>

// 目标接口类,定义客户端期望使用的接口
class Target {
public:
    /**
     * @brief 目标接口方法,客户端会调用此方法
     */
    virtual void request() {
        std::cout << "Target: Default request." << std::endl;
    }

    /**
     * @brief 虚析构函数,确保正确释放派生类对象
     */
    virtual ~Target() {}
};

// 适配者类,拥有客户端不兼容的接口
class Adaptee {
public:
    /**
     * @brief 适配者类的特殊方法,与目标接口不兼容
     */
    void specificRequest() {
        std::cout << "Adaptee: Specific request." << std::endl;
    }
};

// 类适配器类,继承自目标接口类和适配者类
class ClassAdapter : public Target, private Adaptee {
public:
    /**
     * @brief 重写目标接口的 request 方法,调用适配者类的 specificRequest 方法
     */
    void request() override {
        this->specificRequest();
    }
};

int main() {
    // 创建类适配器对象
    Target* adapter = new ClassAdapter();
    // 调用目标接口的方法,实际上调用的是适配者类的方法
    adapter->request();
    // 释放内存
    delete adapter;

    return 0;
}
代码解释
  • Target:定义了客户端期望使用的接口 request
  • Adaptee:拥有与目标接口不兼容的特殊方法 specificRequest
  • ClassAdapter:通过多重继承,继承了 Target 类和 Adaptee 类,并重写了 request 方法,在该方法中调用 Adaptee 类的 specificRequest 方法,从而将适配者类的接口转换为目标接口。
  • main 函数:创建 ClassAdapter 对象,并调用 request 方法进行测试。

对象适配器模式

对象适配器模式通过组合的方式,将适配者对象嵌入到适配器类中,实现接口的转换。

#include <iostream>

// 目标接口类,定义客户端期望使用的接口
class Target {
public:
    /**
     * @brief 目标接口方法,客户端会调用此方法
     */
    virtual void request() {
        std::cout << "Target: Default request." << std::endl;
    }

    /**
     * @brief 虚析构函数,确保正确释放派生类对象
     */
    virtual ~Target() {}
};

// 适配者类,拥有客户端不兼容的接口
class Adaptee {
public:
    /**
     * @brief 适配者类的特殊方法,与目标接口不兼容
     */
    void specificRequest() {
        std::cout << "Adaptee: Specific request." << std::endl;
    }
};

// 对象适配器类,包含适配者对象
class ObjectAdapter : public Target {
private:
    Adaptee* adaptee;  // 适配者对象指针

public:
    /**
     * @brief 构造函数,初始化适配者对象指针
     * @param adaptee 适配者对象指针
     */
    ObjectAdapter(Adaptee* adaptee) : adaptee(adaptee) {}

    /**
     * @brief 重写目标接口的 request 方法,调用适配者对象的 specificRequest 方法
     */
    void request() override {
        adaptee->specificRequest();
    }

    /**
     * @brief 析构函数,释放适配者对象的内存
     */
    ~ObjectAdapter() {
        delete adaptee;
    }
};

int main() {
    // 创建适配者对象
    Adaptee* adaptee = new Adaptee();
    // 创建对象适配器对象,并传入适配者对象指针
    Target* adapter = new ObjectAdapter(adaptee);
    // 调用目标接口的方法,实际上调用的是适配者类的方法
    adapter->request();
    // 释放内存
    delete adapter;

    return 0;
}
代码解释
  • Target:同上述类适配器模式中的 Target 类,定义了客户端期望使用的接口 request
  • Adaptee:拥有与目标接口不兼容的特殊方法 specificRequest
  • ObjectAdapter:继承自 Target 类,包含一个 Adaptee 类的指针。在构造函数中接收一个 Adaptee 对象指针,并在 request 方法中调用该对象的 specificRequest 方法,实现接口的转换。
  • main 函数:创建 Adaptee 对象和 ObjectAdapter 对象,调用 request 方法进行测试,并在最后释放内存。

适配器模式通过适配器类将不兼容的接口转换为客户端期望的接口,使得现有的类可以在不修改其内部结构的情况下在其他类中使用。类适配器模式使用多重继承,对象适配器模式使用组合的方式,各有优缺点,可根据具体需求选择使用。

装饰者模式

作用:动态地给对象添加职责,比生成子类更灵活。

应用:在不改变现有对象结构的情况下,为对象添加新的功能或行为。以咖啡店的饮品为例,饮品有基础的咖啡,还可以通过装饰者添加不同的配料(如牛奶、巧克力等),为饮品动态添加新的功能(价格和描述的改变)。

#include <iostream>
#include <string>

// 抽象组件类:定义饮品的接口
class Beverage {
public:
    /**
     * @brief 获取饮品描述的纯虚函数,具体饮品类需要实现该方法
     * @return 饮品的描述信息
     */
    virtual std::string getDescription() const = 0;

    /**
     * @brief 获取饮品价格的纯虚函数,具体饮品类需要实现该方法
     * @return 饮品的价格
     */
    virtual double cost() const = 0;

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~Beverage() {}
};

// 具体组件类:浓缩咖啡
class Espresso : public Beverage {
public:
    /**
     * @brief 获取浓缩咖啡的描述信息
     * @return 浓缩咖啡的描述
     */
    std::string getDescription() const override {
        return "Espresso";
    }

    /**
     * @brief 获取浓缩咖啡的价格
     * @return 浓缩咖啡的价格
     */
    double cost() const override {
        return 1.99;
    }
};

// 抽象装饰者类:继承自 Beverage 类,用于装饰具体饮品
class CondimentDecorator : public Beverage {
protected:
    Beverage* beverage;  // 被装饰的饮品对象

public:
    /**
     * @brief 构造函数,初始化被装饰的饮品对象
     * @param beverage 被装饰的饮品对象指针
     */
    CondimentDecorator(Beverage* beverage) : beverage(beverage) {}

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~CondimentDecorator() {
        delete beverage;
    }
};

// 具体装饰者类:牛奶
class Milk : public CondimentDecorator {
public:
    /**
     * @brief 构造函数,调用基类构造函数初始化被装饰的饮品对象
     * @param beverage 被装饰的饮品对象指针
     */
    Milk(Beverage* beverage) : CondimentDecorator(beverage) {}

    /**
     * @brief 获取添加牛奶后的饮品描述信息
     * @return 添加牛奶后的饮品描述
     */
    std::string getDescription() const override {
        return beverage->getDescription() + ", Milk";
    }

    /**
     * @brief 获取添加牛奶后的饮品价格
     * @return 添加牛奶后的饮品价格
     */
    double cost() const override {
        return beverage->cost() + 0.3;
    }
};

// 具体装饰者类:巧克力
class Chocolate : public CondimentDecorator {
public:
    /**
     * @brief 构造函数,调用基类构造函数初始化被装饰的饮品对象
     * @param beverage 被装饰的饮品对象指针
     */
    Chocolate(Beverage* beverage) : CondimentDecorator(beverage) {}

    /**
     * @brief 获取添加巧克力后的饮品描述信息
     * @return 添加巧克力后的饮品描述
     */
    std::string getDescription() const override {
        return beverage->getDescription() + ", Chocolate";
    }

    /**
     * @brief 获取添加巧克力后的饮品价格
     * @return 添加巧克力后的饮品价格
     */
    double cost() const override {
        return beverage->cost() + 0.5;
    }
};

int main() {
    // 创建一杯浓缩咖啡
    Beverage* beverage = new Espresso();
    std::cout << beverage->getDescription() << " costs $" << beverage->cost() << std::endl;

    // 给浓缩咖啡添加牛奶
    Beverage* beverageWithMilk = new Milk(beverage);
    std::cout << beverageWithMilk->getDescription() << " costs $" << beverageWithMilk->cost() << std::endl;

    // 再给添加了牛奶的咖啡添加巧克力
    Beverage* beverageWithMilkAndChocolate = new Chocolate(beverageWithMilk);
    std::cout << beverageWithMilkAndChocolate->getDescription() << " costs $" << beverageWithMilkAndChocolate->cost() << std::endl;

    // 释放内存
    delete beverageWithMilkAndChocolate;

    return 0;
}

代码解释

  1. 抽象组件类 Beverage
    • 定义了两个纯虚函数 getDescription()cost(),分别用于获取饮品的描述信息和价格。具体的饮品类需要实现这两个方法。
    • 提供了一个虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
  2. 具体组件类 Espresso
    • 继承自 Beverage 类,实现了 getDescription()cost() 方法,分别返回浓缩咖啡的描述和价格。
  3. 抽象装饰者类 CondimentDecorator
    • 继承自 Beverage 类,包含一个指向 Beverage 对象的指针 beverage,用于存储被装饰的饮品对象。
    • 构造函数接收一个 Beverage 对象指针,并将其赋值给 beverage
    • 提供了虚析构函数,用于释放被装饰的饮品对象的内存。
  4. 具体装饰者类(MilkChocolate
    • 继承自 CondimentDecorator 类,在构造函数中调用基类构造函数初始化被装饰的饮品对象。
    • 重写了 getDescription()cost() 方法,在原饮品的描述和价格基础上添加了自身的描述和价格。
  5. 主函数 main()
    • 创建一杯浓缩咖啡,并输出其描述和价格。
    • 给浓缩咖啡添加牛奶,创建一个新的饮品对象,并输出其描述和价格。
    • 再给添加了牛奶的咖啡添加巧克力,创建一个新的饮品对象,并输出其描述和价格。
    • 最后释放所有对象的内存,避免内存泄漏。

通过装饰者模式,我们可以在不改变现有对象结构的情况下,动态地为对象添加新的功能或行为,比生成子类更加灵活。如果需要添加新的配料,只需创建一个新的具体装饰者类即可。

代理模式

作用:为另外一个对象提供一个替代或占位符。当客户无法直接访问另外一个对象时,代理就可以担任中间人的角色。

应用:实现对象的访问控制,管理对象的生命周期,控制对象的访问,以及在跨平台上实现方法调用。代理模式分为多种类型,这里以远程代理和保护代理为例进行说明。

远程代理示例

远程代理用于在不同地址空间中代表对象,模拟在本地调用远程对象的方法。

#include <iostream>
#include <string>

// 抽象主题类,定义客户端和代理都需要实现的接口
class Subject {
public:
    /**
     * @brief 抽象方法,代表要执行的操作
     */
    virtual void request() = 0;

    /**
     * @brief 虚析构函数,确保正确释放派生类对象
     */
    virtual ~Subject() {}
};

// 真实主题类,实现具体的业务逻辑
class RealSubject : public Subject {
public:
    /**
     * @brief 实现抽象主题类的 request 方法,输出真实主题执行操作的信息
     */
    void request() override {
        std::cout << "RealSubject: Handling request." << std::endl;
    }
};

// 远程代理类,模拟在本地调用远程对象的方法
class RemoteProxy : public Subject {
private:
    RealSubject* realSubject;  // 指向真实主题对象的指针

public:
    /**
     * @brief 构造函数,初始化真实主题对象
     */
    RemoteProxy() {
        realSubject = new RealSubject();
    }

    /**
     * @brief 析构函数,释放真实主题对象的内存
     */
    ~RemoteProxy() {
        delete realSubject;
    }

    /**
     * @brief 实现抽象主题类的 request 方法,调用真实主题的 request 方法
     */
    void request() override {
        std::cout << "RemoteProxy: Delegating request to the real subject." << std::endl;
        realSubject->request();
    }
};

// 客户端代码,使用代理对象
void clientCode(Subject* subject) {
    subject->request();
}

int main() {
    // 创建远程代理对象
    RemoteProxy* proxy = new RemoteProxy();
    // 调用客户端代码,传入代理对象
    clientCode(proxy);
    // 释放代理对象的内存
    delete proxy;

    return 0;
}

代码解释

  • Subject:抽象主题类,定义了客户端和代理都需要实现的接口 request()
  • RealSubject:真实主题类,实现了 request() 方法,包含具体的业务逻辑。
  • RemoteProxy:远程代理类,持有一个 RealSubject 对象的指针。在 request() 方法中,它会先输出代理相关信息,然后调用真实主题的 request() 方法。
  • clientCode 函数:客户端代码,接收一个 Subject 类型的指针,调用其 request() 方法。
  • main 函数:创建远程代理对象,调用客户端代码,最后释放代理对象的内存。

保护代理示例

保护代理用于控制对真实对象的访问权限。

#include <iostream>
#include <string>

// 抽象主题类,定义客户端和代理都需要实现的接口
class Subject {
public:
    /**
     * @brief 抽象方法,代表要执行的操作
     */
    virtual void request() = 0;

    /**
     * @brief 虚析构函数,确保正确释放派生类对象
     */
    virtual ~Subject() {}
};

// 真实主题类,实现具体的业务逻辑
class RealSubject : public Subject {
public:
    /**
     * @brief 实现抽象主题类的 request 方法,输出真实主题执行操作的信息
     */
    void request() override {
        std::cout << "RealSubject: Handling request." << std::endl;
    }
};

// 保护代理类,控制对真实主题的访问
class ProtectionProxy : public Subject {
private:
    RealSubject* realSubject;  // 指向真实主题对象的指针
    std::string password;  // 访问密码

    /**
     * @brief 检查密码是否正确
     * @param inputPassword 用户输入的密码
     * @return 如果密码正确返回 true,否则返回 false
     */
    bool checkAccess(const std::string& inputPassword) {
        return inputPassword == password;
    }

public:
    /**
     * @brief 构造函数,初始化真实主题对象和访问密码
     * @param password 访问密码
     */
    ProtectionProxy(const std::string& password) : password(password) {
        realSubject = new RealSubject();
    }

    /**
     * @brief 析构函数,释放真实主题对象的内存
     */
    ~ProtectionProxy() {
        delete realSubject;
    }

    /**
     * @brief 实现抽象主题类的 request 方法,检查密码后决定是否调用真实主题的方法
     * @param inputPassword 用户输入的密码
     */
    void request(const std::string& inputPassword) {
        if (checkAccess(inputPassword)) {
            std::cout << "ProtectionProxy: Access granted. Delegating request to the real subject." << std::endl;
            realSubject->request();
        } else {
            std::cout << "ProtectionProxy: Access denied." << std::endl;
        }
    }
};

int main() {
    // 创建保护代理对象,设置访问密码
    ProtectionProxy* proxy = new ProtectionProxy("secret");
    // 尝试使用正确密码访问
    proxy->request("secret");
    // 尝试使用错误密码访问
    proxy->request("wrong");
    // 释放代理对象的内存
    delete proxy;

    return 0;
}

代码解释

  • Subject 类和 RealSubject:与远程代理示例中的作用相同。
  • ProtectionProxy:保护代理类,持有一个 RealSubject 对象的指针和一个访问密码。checkAccess() 方法用于检查用户输入的密码是否正确。在 request() 方法中,会先检查密码,若密码正确则调用真实主题的 request() 方法,否则输出访问拒绝信息。
  • main 函数:创建保护代理对象,分别使用正确和错误的密码调用 request() 方法进行测试,最后释放代理对象的内存。

通过代理模式,我们可以在不改变真实对象的情况下,对其进行访问控制、管理生命周期等操作,增强了系统的灵活性和安全性。

外观模式

作用:简化系统接口的模式。它由一个外观类定义,这个类知道要包装的一系列子系统类。客户可以通过外观类直接访问子系统中的功能而不必了解内部细节。

应用:降低时间和精力,减少客户端访问子系统的数量,用于整合子系统的接口。

#include <iostream>

// 子系统类 A,实现具体的功能 A
class SubsystemA {
public:
    /**
     * @brief 执行子系统 A 的操作,输出相应信息
     */
    void operationA() {
        std::cout << "SubsystemA: Performing operation A." << std::endl;
    }
};

// 子系统类 B,实现具体的功能 B
class SubsystemB {
public:
    /**
     * @brief 执行子系统 B 的操作,输出相应信息
     */
    void operationB() {
        std::cout << "SubsystemB: Performing operation B." << std::endl;
    }
};

// 子系统类 C,实现具体的功能 C
class SubsystemC {
public:
    /**
     * @brief 执行子系统 C 的操作,输出相应信息
     */
    void operationC() {
        std::cout << "SubsystemC: Performing operation C." << std::endl;
    }
};

// 外观类,为客户端提供统一的接口,封装子系统的操作
class Facade {
private:
    SubsystemA subsystemA;  // 子系统 A 的对象
    SubsystemB subsystemB;  // 子系统 B 的对象
    SubsystemC subsystemC;  // 子系统 C 的对象

public:
    /**
     * @brief 执行简化的操作,调用子系统 A 和 B 的操作
     */
    void performSimpleOperation() {
        std::cout << "Facade: Initiating simple operation." << std::endl;
        subsystemA.operationA();
        subsystemB.operationB();
    }

    /**
     * @brief 执行复杂的操作,调用子系统 A、B 和 C 的操作
     */
    void performComplexOperation() {
        std::cout << "Facade: Initiating complex operation." << std::endl;
        subsystemA.operationA();
        subsystemB.operationB();
        subsystemC.operationC();
    }
};

// 客户端代码,使用外观类进行操作
void clientCode(Facade& facade) {
    // 执行简化操作
    facade.performSimpleOperation();
    std::cout << std::endl;
    // 执行复杂操作
    facade.performComplexOperation();
}

int main() {
    // 创建外观类对象
    Facade facade;
    // 调用客户端代码,传入外观类对象
    clientCode(facade);

    return 0;
}

代码解释

  1. 子系统类(SubsystemASubsystemBSubsystemC
    • 每个子系统类都有自己的具体操作方法(如 operationA()operationB()operationC()),这些方法实现了各自的功能。客户端通常不需要直接与这些子系统类交互,因为它们的组合和调用逻辑可能比较复杂。
  2. 外观类(Facade
    • 包含了所有子系统类的对象(subsystemAsubsystemBsubsystemC)。
    • 提供了简化的接口方法(performSimpleOperation()performComplexOperation()),这些方法内部调用了子系统类的操作方法,将复杂的子系统操作封装起来。客户端只需调用外观类的这些方法,而无需了解子系统的具体实现和调用顺序。
  3. 客户端代码(clientCode 函数和 main 函数)
    • clientCode 函数接收一个 Facade 对象作为参数,调用其简化和复杂操作方法。
    • main 函数创建了 Facade 对象,并调用 clientCode 函数,展示了客户端如何通过外观类来访问子系统的功能。

外观模式的优势

  • 简化接口:客户端只需与外观类交互,无需了解子系统的复杂细节,降低了客户端的使用难度。
  • 解耦客户端和子系统:客户端和子系统之间的依赖关系被外观类隔离,子系统的变化不会直接影响客户端,提高了系统的可维护性和可扩展性。
  • 提高效率:减少了客户端访问子系统的数量,降低了客户端与子系统之间的交互成本。

通过使用外观模式,我们可以将复杂的子系统封装起来,为客户端提供一个简单易用的接口,从而提高系统的整体性能和可维护性。

桥接模式

作用:把抽象和实现分离开来,以便两者独立地变化。此模式在不同的编程语言之间创建了一个桥,支持平台无关性,以实现代码重用。

应用:通常由抽象类处理,以及一个管理类,该类将实现类注入抽象类中。

#include <iostream>
#include <string>

// 实现类接口,定义具体实现的操作
class Implementor {
public:
    /**
     * @brief 抽象方法,具体实现类需要实现该方法
     */
    virtual void operationImpl() = 0;

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~Implementor() {}
};

// 具体实现类 A
class ConcreteImplementorA : public Implementor {
public:
    /**
     * @brief 实现 Implementor 接口的 operationImpl 方法,输出具体实现 A 的信息
     */
    void operationImpl() override {
        std::cout << "ConcreteImplementorA: Performing operation." << std::endl;
    }
};

// 具体实现类 B
class ConcreteImplementorB : public Implementor {
public:
    /**
     * @brief 实现 Implementor 接口的 operationImpl 方法,输出具体实现 B 的信息
     */
    void operationImpl() override {
        std::cout << "ConcreteImplementorB: Performing operation." << std::endl;
    }
};

// 抽象类,包含一个指向实现类的指针
class Abstraction {
protected:
    Implementor* implementor;  // 指向实现类的指针

public:
    /**
     * @brief 构造函数,初始化实现类指针
     * @param implementor 实现类对象指针
     */
    Abstraction(Implementor* implementor) : implementor(implementor) {}

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~Abstraction() {
        delete implementor;
    }

    /**
     * @brief 抽象方法,调用实现类的操作方法
     */
    virtual void operation() {
        implementor->operationImpl();
    }
};

// 扩展抽象类,继承自 Abstraction 类
class RefinedAbstraction : public Abstraction {
public:
    /**
     * @brief 构造函数,调用基类构造函数初始化实现类指针
     * @param implementor 实现类对象指针
     */
    RefinedAbstraction(Implementor* implementor) : Abstraction(implementor) {}

    /**
     * @brief 重写 operation 方法,在调用实现类操作前后添加额外逻辑
     */
    void operation() override {
        std::cout << "RefinedAbstraction: Before operation." << std::endl;
        Abstraction::operation();
        std::cout << "RefinedAbstraction: After operation." << std::endl;
    }
};

int main() {
    // 创建具体实现类 A 的对象
    Implementor* implementorA = new ConcreteImplementorA();
    // 创建扩展抽象类对象,使用具体实现类 A
    Abstraction* abstractionA = new RefinedAbstraction(implementorA);
    // 调用扩展抽象类的操作方法
    abstractionA->operation();
    std::cout << std::endl;

    // 创建具体实现类 B 的对象
    Implementor* implementorB = new ConcreteImplementorB();
    // 创建扩展抽象类对象,使用具体实现类 B
    Abstraction* abstractionB = new RefinedAbstraction(implementorB);
    // 调用扩展抽象类的操作方法
    abstractionB->operation();

    // 释放内存
    delete abstractionA;
    delete abstractionB;

    return 0;
}

代码解释

  1. 实现类接口 Implementor
    • 定义了一个纯虚函数 operationImpl(),具体的实现类需要实现这个方法,以完成具体的操作。
    • 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
  2. 具体实现类(ConcreteImplementorAConcreteImplementorB
    • 继承自 Implementor 类,实现了 operationImpl() 方法,分别输出不同的操作信息。
  3. 抽象类 Abstraction
    • 包含一个指向 Implementor 类的指针 implementor,用于引用具体的实现类对象。
    • 构造函数接收一个 Implementor 对象指针并初始化 implementor
    • 提供了虚析构函数,用于释放 implementor 所指向的对象的内存。
    • 定义了 operation() 方法,该方法调用 implementoroperationImpl() 方法,将抽象部分和实现部分连接起来。
  4. 扩展抽象类 RefinedAbstraction
    • 继承自 Abstraction 类,在构造函数中调用基类构造函数初始化 implementor
    • 重写了 operation() 方法,在调用基类的 operation() 方法前后添加了额外的逻辑,展示了抽象部分可以独立于实现部分进行扩展。
  5. 主函数 main()
    • 创建具体实现类 ConcreteImplementorAConcreteImplementorB 的对象。
    • 分别创建 RefinedAbstraction 对象,将不同的具体实现类对象注入其中。
    • 调用 RefinedAbstraction 对象的 operation() 方法,展示了抽象部分和实现部分可以独立变化的特性。
    • 最后释放所有对象的内存,避免内存泄漏。

桥接模式的优势

  • 分离抽象和实现:抽象部分和实现部分可以独立地进行扩展和修改,互不影响,提高了系统的可维护性和可扩展性。
  • 支持多维度变化:可以在抽象层和实现层分别进行不同的扩展,从而实现多维度的变化组合。
  • 代码重用:不同的抽象和实现可以进行组合,提高了代码的复用性。

通过桥接模式,我们可以有效地将抽象和实现分离,使系统更加灵活和易于维护。

组合模式

作用:将对象组合成树形结构,以表示部分-整体的层次结构,适用于需要处理树形结构的场景。

应用:例如,文件系统中的目录和文件可以看作是组合模式的应用。

#include <iostream>
#include <vector>
#include <string>

// 抽象组件类,定义文件和目录的通用接口
class FileSystemComponent {
public:
    /**
     * @brief 纯虚函数,用于显示组件信息,具体子类需要实现
     */
    virtual void displayInfo() const = 0;

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~FileSystemComponent() {}

    /**
     * @brief 用于向目录添加子组件的虚函数,默认实现为空,因为文件不能添加子组件
     * @param component 要添加的子组件指针
     */
    virtual void add(FileSystemComponent* component) {}

    /**
     * @brief 用于从目录移除子组件的虚函数,默认实现为空,因为文件不能移除子组件
     * @param component 要移除的子组件指针
     */
    virtual void remove(FileSystemComponent* component) {}
};

// 叶子节点类,代表文件
class File : public FileSystemComponent {
private:
    std::string name;  // 文件名称

public:
    /**
     * @brief 构造函数,初始化文件名称
     * @param name 文件名称
     */
    File(const std::string& name) : name(name) {}

    /**
     * @brief 实现抽象组件类的 displayInfo 方法,显示文件信息
     */
    void displayInfo() const override {
        std::cout << "File: " << name << std::endl;
    }
};

// 组合节点类,代表目录
class Directory : public FileSystemComponent {
private:
    std::string name;  // 目录名称
    std::vector<FileSystemComponent*> children;  // 存储子组件的向量

public:
    /**
     * @brief 构造函数,初始化目录名称
     * @param name 目录名称
     */
    Directory(const std::string& name) : name(name) {}

    /**
     * @brief 析构函数,释放所有子组件的内存
     */
    ~Directory() override {
        for (auto child : children) {
            delete child;
        }
    }

    /**
     * @brief 实现抽象组件类的 displayInfo 方法,显示目录信息及子组件信息
     */
    void displayInfo() const override {
        std::cout << "Directory: " << name << std::endl;
        for (auto child : children) {
            std::cout << "  ";
            child->displayInfo();
        }
    }

    /**
     * @brief 实现添加子组件的方法,将子组件添加到目录中
     * @param component 要添加的子组件指针
     */
    void add(FileSystemComponent* component) override {
        children.push_back(component);
    }

    /**
     * @brief 实现移除子组件的方法,从目录中移除指定子组件
     * @param component 要移除的子组件指针
     */
    void remove(FileSystemComponent* component) override {
        for (auto it = children.begin(); it != children.end(); ++it) {
            if (*it == component) {
                delete *it;
                children.erase(it);
                break;
            }
        }
    }
};

int main() {
    // 创建根目录
    Directory* root = new Directory("Root");

    // 创建子目录
    Directory* subDir1 = new Directory("SubDir1");
    Directory* subDir2 = new Directory("SubDir2");

    // 创建文件
    File* file1 = new File("File1.txt");
    File* file2 = new File("File2.txt");
    File* file3 = new File("File3.txt");

    // 将文件添加到子目录
    subDir1->add(file1);
    subDir1->add(file2);
    subDir2->add(file3);

    // 将子目录添加到根目录
    root->add(subDir1);
    root->add(subDir2);

    // 显示根目录及其子组件信息
    root->displayInfo();

    // 释放根目录及其子组件的内存
    delete root;

    return 0;
}

代码解释

  1. 抽象组件类 FileSystemComponent
    • 定义了 displayInfo() 纯虚函数,这是文件和目录都需要实现的通用操作,用于显示自身信息。
    • 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
    • 定义了 add()remove() 虚函数,默认实现为空,因为文件不需要这些操作,而目录需要重写这些方法。
  2. 叶子节点类 File
    • 继承自 FileSystemComponent 类,包含一个 name 成员变量表示文件名称。
    • 实现了 displayInfo() 方法,用于显示文件的名称信息。
  3. 组合节点类 Directory
    • 继承自 FileSystemComponent 类,包含一个 name 成员变量表示目录名称,以及一个 children 向量用于存储子组件。
    • 重写了 displayInfo() 方法,不仅显示目录自身的信息,还会递归显示所有子组件的信息。
    • 重写了 add() 方法,用于将子组件添加到目录中。
    • 重写了 remove() 方法,用于从目录中移除指定的子组件,并释放其内存。
  4. 主函数 main()
    • 创建了根目录、子目录和文件对象。
    • 将文件添加到子目录,再将子目录添加到根目录,构建了一个树形的文件系统结构。
    • 调用根目录的 displayInfo() 方法,显示整个文件系统的信息。
    • 最后释放根目录及其子组件的内存,避免内存泄漏。

组合模式的优势

  • 一致性处理:客户端可以统一地处理单个对象(文件)和组合对象(目录),无需区分它们,简化了客户端代码。
  • 灵活扩展:可以方便地添加新的文件或目录,符合开闭原则,即对扩展开放,对修改关闭。
  • 表示层次结构:能够清晰地表示部分 - 整体的层次结构,如文件系统中的目录和文件的关系。

通过组合模式,我们可以方便地处理树形结构的对象,提高代码的可维护性和可扩展性。

享元模式

作用:通过共享技术有效地支持大量细粒度的对象。

应用:减少创建对象的数量,以减少内存占用和提高性能。以图形绘制为例,不同颜色的圆形可以共享圆形的绘制逻辑,仅在颜色上有所不同。

#include <iostream>
#include <string>
#include <unordered_map>

// 抽象享元类,定义享元对象的接口
class Shape {
public:
    /**
     * @brief 纯虚函数,具体享元类需要实现该方法,用于绘制图形
     */
    virtual void draw() = 0;

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~Shape() {}
};

// 具体享元类,代表圆形
class Circle : public Shape {
private:
    std::string color;  // 圆形的颜色

public:
    /**
     * @brief 构造函数,初始化圆形的颜色
     * @param color 圆形的颜色
     */
    Circle(const std::string& color) : color(color) {}

    /**
     * @brief 实现抽象享元类的 draw 方法,输出绘制圆形的信息
     */
    void draw() override {
        std::cout << "Drawing a circle with color: " << color << std::endl;
    }
};

// 享元工厂类,负责创建和管理享元对象
class ShapeFactory {
private:
    std::unordered_map<std::string, Shape*> circleMap;  // 存储享元对象的哈希表

public:
    /**
     * @brief 获取指定颜色的圆形对象,如果对象已存在则直接返回,否则创建新对象
     * @param color 圆形的颜色
     * @return 指向圆形对象的指针
     */
    Shape* getCircle(const std::string& color) {
        auto it = circleMap.find(color);
        if (it == circleMap.end()) {
            // 如果该颜色的圆形对象不存在,则创建新对象并存储到哈希表中
            Circle* circle = new Circle(color);
            circleMap[color] = circle;
            std::cout << "Creating circle of color: " << color << std::endl;
            return circle;
        }
        // 如果对象已存在,直接返回
        return it->second;
    }

    /**
     * @brief 析构函数,释放所有享元对象的内存
     */
    ~ShapeFactory() {
        for (auto pair : circleMap) {
            delete pair.second;
        }
    }
};

// 客户端代码,使用享元工厂创建和使用圆形对象
void clientCode(ShapeFactory& factory) {
    // 获取红色圆形对象
    Shape* redCircle = factory.getCircle("Red");
    redCircle->draw();

    // 再次获取红色圆形对象,应该直接返回已存在的对象
    Shape* anotherRedCircle = factory.getCircle("Red");
    anotherRedCircle->draw();

    // 获取绿色圆形对象
    Shape* greenCircle = factory.getCircle("Green");
    greenCircle->draw();
}

int main() {
    // 创建享元工厂对象
    ShapeFactory factory;
    // 调用客户端代码,使用享元工厂创建和使用圆形对象
    clientCode(factory);

    return 0;
}

代码解释

  1. 抽象享元类 Shape
    • 定义了一个纯虚函数 draw(),具体的享元类需要实现这个方法来完成图形的绘制操作。
    • 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
  2. 具体享元类 Circle
    • 继承自 Shape 类,包含一个 color 成员变量表示圆形的颜色。
    • 实现了 draw() 方法,输出绘制指定颜色圆形的信息。
  3. 享元工厂类 ShapeFactory
    • 使用 std::unordered_map 存储不同颜色的圆形对象,键为颜色,值为指向圆形对象的指针。
    • getCircle() 方法用于获取指定颜色的圆形对象。如果该颜色的对象已存在于哈希表中,则直接返回;否则,创建新的圆形对象并存储到哈希表中。
    • 析构函数负责释放所有享元对象的内存,避免内存泄漏。
  4. 客户端代码(clientCode 函数和 main 函数)
    • clientCode 函数接收一个 ShapeFactory 对象作为参数,通过调用 getCircle() 方法获取不同颜色的圆形对象,并调用其 draw() 方法进行绘制。可以看到,当多次请求相同颜色的圆形对象时,不会重复创建,而是直接返回已存在的对象。
    • main 函数创建了 ShapeFactory 对象,并调用 clientCode 函数进行测试。

享元模式的优势

  • 节省内存:通过共享对象,避免了创建大量相同或相似的对象,减少了内存占用。
  • 提高性能:减少了对象的创建和销毁开销,提高了系统的性能。
  • 便于管理:享元工厂类负责对象的创建和管理,使得对象的使用更加方便和统一。

通过享元模式,我们可以有效地支持大量细粒度的对象,同时降低系统的资源消耗。

观察者模式

作用:定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖于它的对象都会得到通知并自动更新。

应用:实现分布式事件处理系统,如MVC架构中的模型和视图。模拟一个简单的新闻发布系统,新闻发布者(被观察对象)发布新闻时,所有订阅者(观察者)会收到通知并进行相应处理。

#include <iostream>
#include <vector>
#include <string>

// 前置声明
class Observer;

// 抽象主题类(被观察对象)
class Subject {
public:
    /**
     * @brief 注册观察者
     * @param observer 要注册的观察者指针
     */
    virtual void attach(Observer* observer) = 0;

    /**
     * @brief 移除观察者
     * @param observer 要移除的观察者指针
     */
    virtual void detach(Observer* observer) = 0;

    /**
     * @brief 通知所有观察者
     */
    virtual void notify() = 0;

    /**
     * @brief 虚析构函数,确保正确释放派生类对象
     */
    virtual ~Subject() {}
};

// 抽象观察者类
class Observer {
public:
    /**
     * @brief 更新方法,当被观察对象状态改变时会调用此方法
     * @param message 被观察对象传递的消息
     */
    virtual void update(const std::string& message) = 0;

    /**
     * @brief 虚析构函数,确保正确释放派生类对象
     */
    virtual ~Observer() {}
};

// 具体主题类(新闻发布者)
class NewsPublisher : public Subject {
private:
    std::vector<Observer*> observers;  // 存储所有观察者的列表
    std::string news;  // 存储当前的新闻内容

public:
    /**
     * @brief 注册观察者,将观察者添加到列表中
     * @param observer 要注册的观察者指针
     */
    void attach(Observer* observer) override {
        observers.push_back(observer);
    }

    /**
     * @brief 移除观察者,从列表中删除指定的观察者
     * @param observer 要移除的观察者指针
     */
    void detach(Observer* observer) override {
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                break;
            }
        }
    }

    /**
     * @brief 通知所有观察者,遍历观察者列表并调用其 update 方法
     */
    void notify() override {
        for (auto observer : observers) {
            observer->update(news);
        }
    }

    /**
     * @brief 发布新闻,更新新闻内容并通知所有观察者
     * @param news 要发布的新闻内容
     */
    void publishNews(const std::string& news) {
        this->news = news;
        notify();
    }
};

// 具体观察者类(新闻订阅者)
class NewsSubscriber : public Observer {
private:
    std::string name;  // 订阅者的名称

public:
    /**
     * @brief 构造函数,初始化订阅者的名称
     * @param name 订阅者的名称
     */
    NewsSubscriber(const std::string& name) : name(name) {}

    /**
     * @brief 更新方法,当收到新闻通知时,输出相应的信息
     * @param message 收到的新闻内容
     */
    void update(const std::string& message) override {
        std::cout << name << " received news: " << message << std::endl;
    }
};

int main() {
    // 创建新闻发布者对象
    NewsPublisher publisher;

    // 创建新闻订阅者对象
    NewsSubscriber subscriber1("Subscriber 1");
    NewsSubscriber subscriber2("Subscriber 2");

    // 订阅者注册到新闻发布者
    publisher.attach(&subscriber1);
    publisher.attach(&subscriber2);

    // 发布新闻
    publisher.publishNews("New technology breakthrough!");

    // 移除一个订阅者
    publisher.detach(&subscriber2);

    // 再次发布新闻
    publisher.publishNews("Another important event!");

    return 0;
}

代码解释

  1. 抽象主题类 Subject
    • 定义了三个纯虚函数 attachdetachnotify,分别用于注册观察者、移除观察者和通知所有观察者。
    • 提供了一个虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
  2. 抽象观察者类 Observer
    • 定义了一个纯虚函数 update,用于在被观察对象状态改变时更新自身状态。
    • 同样提供了虚析构函数。
  3. 具体主题类 NewsPublisher
    • 使用 std::vector 存储所有注册的观察者。
    • 实现了 attach 方法,将观察者添加到列表中。
    • 实现了 detach 方法,从列表中移除指定的观察者。
    • 实现了 notify 方法,遍历观察者列表并调用每个观察者的 update 方法。
    • publishNews 方法用于更新新闻内容并调用 notify 方法通知所有观察者。
  4. 具体观察者类 NewsSubscriber
    • 包含一个 name 成员变量,用于标识订阅者的名称。
    • 实现了 update 方法,当收到新闻通知时,输出相应的信息。
  5. 主函数 main
    • 创建了一个新闻发布者对象和两个新闻订阅者对象。
    • 将订阅者注册到新闻发布者。
    • 发布一条新闻,所有订阅者会收到通知。
    • 移除一个订阅者后,再次发布新闻,只有剩余的订阅者会收到通知。

通过这种方式,我们实现了对象间的一对多依赖关系,当新闻发布者的状态(新闻内容)改变时,所有依赖于它的订阅者都会得到通知并自动更新。这种模式在分布式事件处理系统、MVC 架构等场景中非常有用。

命令模式

作用:将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。

应用:实现请求的排队、记录日志、撤销和重做等功能。模拟了一个简单的家电控制系统,通过命令模式实现对家电(如电灯、风扇)的开关操作,同时支持撤销操作,还能体现请求的排队和日志记录的思想。

#include <iostream>
#include <vector>
#include <string>

// 前置声明
class Command;

// 接收者类,代表具体的家电,这里以电灯为例
class Light {
public:
    /**
     * @brief 打开电灯的方法,输出相应信息
     */
    void turnOn() {
        std::cout << "Light is on." << std::endl;
    }

    /**
     * @brief 关闭电灯的方法,输出相应信息
     */
    void turnOff() {
        std::cout << "Light is off." << std::endl;
    }
};

// 抽象命令类,定义命令的接口
class Command {
public:
    /**
     * @brief 纯虚函数,具体命令类需要实现该方法来执行命令
     */
    virtual void execute() = 0;

    /**
     * @brief 纯虚函数,具体命令类需要实现该方法来撤销命令
     */
    virtual void undo() = 0;

    /**
     * @brief 虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存
     */
    virtual ~Command() {}
};

// 具体命令类:打开电灯命令
class LightOnCommand : public Command {
private:
    Light* light;  // 指向电灯对象的指针

public:
    /**
     * @brief 构造函数,初始化电灯对象指针
     * @param light 电灯对象指针
     */
    LightOnCommand(Light* light) : light(light) {}

    /**
     * @brief 实现抽象命令类的 execute 方法,调用电灯的打开方法
     */
    void execute() override {
        light->turnOn();
    }

    /**
     * @brief 实现抽象命令类的 undo 方法,调用电灯的关闭方法来撤销打开操作
     */
    void undo() override {
        light->turnOff();
    }
};

// 具体命令类:关闭电灯命令
class LightOffCommand : public Command {
private:
    Light* light;  // 指向电灯对象的指针

public:
    /**
     * @brief 构造函数,初始化电灯对象指针
     * @param light 电灯对象指针
     */
    LightOffCommand(Light* light) : light(light) {}

    /**
     * @brief 实现抽象命令类的 execute 方法,调用电灯的关闭方法
     */
    void execute() override {
        light->turnOff();
    }

    /**
     * @brief 实现抽象命令类的 undo 方法,调用电灯的打开方法来撤销关闭操作
     */
    void undo() override {
        light->turnOn();
    }
};

// 调用者类,负责发起命令请求
class RemoteControl {
private:
    std::vector<Command*> commands;  // 存储命令的向量,用于请求排队
    std::vector<Command*> undoCommands;  // 存储撤销命令的向量

public:
    /**
     * @brief 发起命令请求,执行命令并记录到命令向量和撤销命令向量中
     * @param command 要执行的命令对象指针
     */
    void setCommand(Command* command) {
        command->execute();
        commands.push_back(command);
        undoCommands.push_back(command);
    }

    /**
     * @brief 撤销上一个命令,从撤销命令向量中取出最后一个命令并调用其 undo 方法
     */
    void undoLastCommand() {
        if (!undoCommands.empty()) {
            Command* lastCommand = undoCommands.back();
            lastCommand->undo();
            undoCommands.pop_back();
        }
    }

    /**
     * @brief 析构函数,释放所有命令对象的内存
     */
    ~RemoteControl() {
        for (auto command : commands) {
            delete command;
        }
    }
};

int main() {
    // 创建电灯对象
    Light* light = new Light();

    // 创建打开和关闭电灯的命令对象
    Command* lightOn = new LightOnCommand(light);
    Command* lightOff = new LightOffCommand(light);

    // 创建遥控器对象
    RemoteControl remote;

    // 发起打开电灯的命令
    remote.setCommand(lightOn);
    // 发起关闭电灯的命令
    remote.setCommand(lightOff);

    // 撤销上一个命令(关闭电灯命令),即再次打开电灯
    remote.undoLastCommand();

    // 释放电灯对象的内存
    delete light;

    return 0;
}

代码解释

  1. 接收者类 Light
    • 表示具体的家电(电灯),包含 turnOn()turnOff() 方法,用于执行打开和关闭电灯的操作。
  2. 抽象命令类 Command
    • 定义了两个纯虚函数 execute()undo(),分别用于执行命令和撤销命令。具体的命令类需要实现这两个方法。
    • 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
  3. 具体命令类(LightOnCommandLightOffCommand
    • 继承自 Command 类,包含一个指向 Light 对象的指针。
    • LightOnCommandexecute() 方法调用电灯的 turnOn() 方法,undo() 方法调用 turnOff() 方法。
    • LightOffCommandexecute() 方法调用电灯的 turnOff() 方法,undo() 方法调用 turnOn() 方法。
  4. 调用者类 RemoteControl
    • 使用 std::vector 存储命令对象,commands 向量用于记录所有执行过的命令,undoCommands 向量用于支持撤销操作。
    • setCommand() 方法接收一个命令对象指针,执行该命令并将其添加到 commandsundoCommands 向量中。
    • undoLastCommand() 方法从 undoCommands 向量中取出最后一个命令并调用其 undo() 方法,实现撤销操作。
    • 析构函数负责释放所有命令对象的内存,避免内存泄漏。
  5. 主函数 main()
    • 创建电灯对象和打开、关闭电灯的命令对象。
    • 创建遥控器对象,通过遥控器发起打开和关闭电灯的命令。
    • 调用遥控器的 undoLastCommand() 方法撤销上一个命令。
    • 最后释放电灯对象的内存。

命令模式的优势

  • 解耦请求者和接收者:调用者(如遥控器)只需要知道如何发起命令,而不需要知道具体的接收者(如电灯)和命令的执行细节。
  • 支持请求排队和撤销重做:可以将命令存储在队列中,实现请求的排队执行;通过记录命令的执行顺序,能够方便地实现撤销和重做操作。
  • 便于扩展:如果需要添加新的命令或接收者,只需要创建新的具体命令类和接收者类,而不需要修改现有的调用者代码。

通过命令模式,我们可以灵活地管理和操作请求,实现复杂的功能,如请求的排队、日志记录、撤销和重做等。

总结

设计模式在C++编程中扮演着重要的角色,它们不仅提高了代码的可读性和可维护性,还能帮助开发者更好地应对复杂的软件设计和开发挑战。通过学习和掌握这些设计模式,开发者可以编写出更加健壮、灵活和可扩展的软件系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值