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()
。ConcreteProductA
和ConcreteProductB
是具体产品类,实现了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()
。ConcreteProductA
和ConcreteProductB
是具体产品类,实现了use()
方法。Factory
是抽象工厂类,定义了创建产品的接口createProduct()
。ConcreteFactoryA
和ConcreteFactoryB
是具体工厂类,分别实现了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;
}
代码解释:
AbstractProductA
和AbstractProductB
是抽象产品类,分别定义了产品 A 和产品 B 的通用接口。ConcreteProductA1
、ConcreteProductA2
、ConcreteProductB1
和ConcreteProductB2
是具体产品类,实现了相应的抽象产品类的方法。AbstractFactory
是抽象工厂类,定义了创建一系列产品的接口。ConcreteFactory1
和ConcreteFactory2
是具体工厂类,分别实现了抽象工厂类的方法来创建不同组合的产品。
策略模式
定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。
应用:例如,设计一种空调,支持多种模式(冷风、热风、无风),可以使用策略模式来实现这些模式的动态切换,从而避免使用多重条件转移语句,使代码更易于维护。
#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;
}
代码解释
- 抽象策略类
AirConditionerMode
:定义了一个纯虚函数operate()
,具体的空调模式类需要实现这个方法来执行相应的操作。同时,提供了一个虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。 - 具体策略类(
ColdWindMode
、HotWindMode
、NoWindMode
):继承自抽象策略类AirConditionerMode
,并实现了operate()
方法,分别输出不同模式下空调的操作信息。 - 上下文类
AirConditioner
:- 包含一个指向
AirConditionerMode
类型的指针currentMode
,用于存储当前使用的空调模式。 - 构造函数用于初始化空调的初始模式。
- 析构函数负责释放当前模式对象的内存。
setMode()
方法用于切换空调的模式,在切换时会先释放旧模式的内存,再更新当前模式。operate()
方法调用当前模式的operate()
方法来执行相应的操作。
- 包含一个指向
- 主函数
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;
}
代码解释
- 抽象组件类
Beverage
:- 定义了两个纯虚函数
getDescription()
和cost()
,分别用于获取饮品的描述信息和价格。具体的饮品类需要实现这两个方法。 - 提供了一个虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
- 定义了两个纯虚函数
- 具体组件类
Espresso
:- 继承自
Beverage
类,实现了getDescription()
和cost()
方法,分别返回浓缩咖啡的描述和价格。
- 继承自
- 抽象装饰者类
CondimentDecorator
:- 继承自
Beverage
类,包含一个指向Beverage
对象的指针beverage
,用于存储被装饰的饮品对象。 - 构造函数接收一个
Beverage
对象指针,并将其赋值给beverage
。 - 提供了虚析构函数,用于释放被装饰的饮品对象的内存。
- 继承自
- 具体装饰者类(
Milk
和Chocolate
):- 继承自
CondimentDecorator
类,在构造函数中调用基类构造函数初始化被装饰的饮品对象。 - 重写了
getDescription()
和cost()
方法,在原饮品的描述和价格基础上添加了自身的描述和价格。
- 继承自
- 主函数
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;
}
代码解释
- 子系统类(
SubsystemA
、SubsystemB
、SubsystemC
):- 每个子系统类都有自己的具体操作方法(如
operationA()
、operationB()
、operationC()
),这些方法实现了各自的功能。客户端通常不需要直接与这些子系统类交互,因为它们的组合和调用逻辑可能比较复杂。
- 每个子系统类都有自己的具体操作方法(如
- 外观类(
Facade
):- 包含了所有子系统类的对象(
subsystemA
、subsystemB
、subsystemC
)。 - 提供了简化的接口方法(
performSimpleOperation()
和performComplexOperation()
),这些方法内部调用了子系统类的操作方法,将复杂的子系统操作封装起来。客户端只需调用外观类的这些方法,而无需了解子系统的具体实现和调用顺序。
- 包含了所有子系统类的对象(
- 客户端代码(
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;
}
代码解释
- 实现类接口
Implementor
:- 定义了一个纯虚函数
operationImpl()
,具体的实现类需要实现这个方法,以完成具体的操作。 - 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
- 定义了一个纯虚函数
- 具体实现类(
ConcreteImplementorA
和ConcreteImplementorB
):- 继承自
Implementor
类,实现了operationImpl()
方法,分别输出不同的操作信息。
- 继承自
- 抽象类
Abstraction
:- 包含一个指向
Implementor
类的指针implementor
,用于引用具体的实现类对象。 - 构造函数接收一个
Implementor
对象指针并初始化implementor
。 - 提供了虚析构函数,用于释放
implementor
所指向的对象的内存。 - 定义了
operation()
方法,该方法调用implementor
的operationImpl()
方法,将抽象部分和实现部分连接起来。
- 包含一个指向
- 扩展抽象类
RefinedAbstraction
:- 继承自
Abstraction
类,在构造函数中调用基类构造函数初始化implementor
。 - 重写了
operation()
方法,在调用基类的operation()
方法前后添加了额外的逻辑,展示了抽象部分可以独立于实现部分进行扩展。
- 继承自
- 主函数
main()
:- 创建具体实现类
ConcreteImplementorA
和ConcreteImplementorB
的对象。 - 分别创建
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;
}
代码解释
- 抽象组件类
FileSystemComponent
:- 定义了
displayInfo()
纯虚函数,这是文件和目录都需要实现的通用操作,用于显示自身信息。 - 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
- 定义了
add()
和remove()
虚函数,默认实现为空,因为文件不需要这些操作,而目录需要重写这些方法。
- 定义了
- 叶子节点类
File
:- 继承自
FileSystemComponent
类,包含一个name
成员变量表示文件名称。 - 实现了
displayInfo()
方法,用于显示文件的名称信息。
- 继承自
- 组合节点类
Directory
:- 继承自
FileSystemComponent
类,包含一个name
成员变量表示目录名称,以及一个children
向量用于存储子组件。 - 重写了
displayInfo()
方法,不仅显示目录自身的信息,还会递归显示所有子组件的信息。 - 重写了
add()
方法,用于将子组件添加到目录中。 - 重写了
remove()
方法,用于从目录中移除指定的子组件,并释放其内存。
- 继承自
- 主函数
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;
}
代码解释
- 抽象享元类
Shape
:- 定义了一个纯虚函数
draw()
,具体的享元类需要实现这个方法来完成图形的绘制操作。 - 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
- 定义了一个纯虚函数
- 具体享元类
Circle
:- 继承自
Shape
类,包含一个color
成员变量表示圆形的颜色。 - 实现了
draw()
方法,输出绘制指定颜色圆形的信息。
- 继承自
- 享元工厂类
ShapeFactory
:- 使用
std::unordered_map
存储不同颜色的圆形对象,键为颜色,值为指向圆形对象的指针。 getCircle()
方法用于获取指定颜色的圆形对象。如果该颜色的对象已存在于哈希表中,则直接返回;否则,创建新的圆形对象并存储到哈希表中。- 析构函数负责释放所有享元对象的内存,避免内存泄漏。
- 使用
- 客户端代码(
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;
}
代码解释
- 抽象主题类
Subject
:- 定义了三个纯虚函数
attach
、detach
和notify
,分别用于注册观察者、移除观察者和通知所有观察者。 - 提供了一个虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
- 定义了三个纯虚函数
- 抽象观察者类
Observer
:- 定义了一个纯虚函数
update
,用于在被观察对象状态改变时更新自身状态。 - 同样提供了虚析构函数。
- 定义了一个纯虚函数
- 具体主题类
NewsPublisher
:- 使用
std::vector
存储所有注册的观察者。 - 实现了
attach
方法,将观察者添加到列表中。 - 实现了
detach
方法,从列表中移除指定的观察者。 - 实现了
notify
方法,遍历观察者列表并调用每个观察者的update
方法。 publishNews
方法用于更新新闻内容并调用notify
方法通知所有观察者。
- 使用
- 具体观察者类
NewsSubscriber
:- 包含一个
name
成员变量,用于标识订阅者的名称。 - 实现了
update
方法,当收到新闻通知时,输出相应的信息。
- 包含一个
- 主函数
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;
}
代码解释
- 接收者类
Light
:- 表示具体的家电(电灯),包含
turnOn()
和turnOff()
方法,用于执行打开和关闭电灯的操作。
- 表示具体的家电(电灯),包含
- 抽象命令类
Command
:- 定义了两个纯虚函数
execute()
和undo()
,分别用于执行命令和撤销命令。具体的命令类需要实现这两个方法。 - 提供了虚析构函数,确保在删除基类指针时能正确释放派生类对象的内存。
- 定义了两个纯虚函数
- 具体命令类(
LightOnCommand
和LightOffCommand
):- 继承自
Command
类,包含一个指向Light
对象的指针。 LightOnCommand
的execute()
方法调用电灯的turnOn()
方法,undo()
方法调用turnOff()
方法。LightOffCommand
的execute()
方法调用电灯的turnOff()
方法,undo()
方法调用turnOn()
方法。
- 继承自
- 调用者类
RemoteControl
:- 使用
std::vector
存储命令对象,commands
向量用于记录所有执行过的命令,undoCommands
向量用于支持撤销操作。 setCommand()
方法接收一个命令对象指针,执行该命令并将其添加到commands
和undoCommands
向量中。undoLastCommand()
方法从undoCommands
向量中取出最后一个命令并调用其undo()
方法,实现撤销操作。- 析构函数负责释放所有命令对象的内存,避免内存泄漏。
- 使用
- 主函数
main()
:- 创建电灯对象和打开、关闭电灯的命令对象。
- 创建遥控器对象,通过遥控器发起打开和关闭电灯的命令。
- 调用遥控器的
undoLastCommand()
方法撤销上一个命令。 - 最后释放电灯对象的内存。
命令模式的优势
- 解耦请求者和接收者:调用者(如遥控器)只需要知道如何发起命令,而不需要知道具体的接收者(如电灯)和命令的执行细节。
- 支持请求排队和撤销重做:可以将命令存储在队列中,实现请求的排队执行;通过记录命令的执行顺序,能够方便地实现撤销和重做操作。
- 便于扩展:如果需要添加新的命令或接收者,只需要创建新的具体命令类和接收者类,而不需要修改现有的调用者代码。
通过命令模式,我们可以灵活地管理和操作请求,实现复杂的功能,如请求的排队、日志记录、撤销和重做等。
总结
设计模式在C++编程中扮演着重要的角色,它们不仅提高了代码的可读性和可维护性,还能帮助开发者更好地应对复杂的软件设计和开发挑战。通过学习和掌握这些设计模式,开发者可以编写出更加健壮、灵活和可扩展的软件系统。