1 C/C++设计模式完全指南
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
1994 年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人(合称 GoF,Gang of Four)在《设计模式:可复用面向对象软件的基础》一书中提出了 23 种经典的设计模式,这些模式成为了面向对象设计的基石。
本指南将详细讲解这 23 种设计模式,结合 C/C++ 语言的特性,提供完整的代码实现、适用场景分析以及优缺点对比,帮助你彻底掌握设计模式,写出更优雅、可维护、可扩展的代码。
1.1 一、面向对象设计原则
设计模式的本质是遵循面向对象的设计原则,这些原则是我们设计软件的指导思想,也是设计模式的理论基础。
1.1.1 1. 单一职责原则(Single Responsibility Principle, SRP)
一个类只负责一个功能领域中的相应职责,也就是说,一个类应该只有一个引起它变化的原因。
1.1.2 2. 开闭原则(Open-Closed Principle, OCP)
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。也就是说,我们可以在不修改原有代码的基础上,扩展系统的功能。这是面向对象设计中最核心的原则。
1.1.3 3. 里氏代换原则(Liskov Substitution Principle, LSP)
所有引用基类对象的地方,都能够透明地使用其子类的对象。也就是说,子类可以替换父类,而不会影响程序的正确性。
1.1.4 4. 依赖倒转原则(Dependence Inversion Principle, DIP)
高层模块不应该依赖低层模块,二者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。简单来说,就是面向接口编程,而不是面向实现编程。
1.1.5 5. 接口隔离原则(Interface Segregation Principle, ISP)
使用多个专门的接口,而不使用单一的总接口。客户端不应该依赖它不需要的接口,不要强迫客户端实现它们用不到的方法。
1.1.6 6. 合成复用原则(Composite Reuse Principle, CRP)
尽量使用对象组合 / 聚合,而不是继承来达到复用的目的。优先使用 "有一个"(has-a),而不是 "是一个"(is-a),因为组合比继承更加灵活。
1.1.7 7. 迪米特法则(Law of Demeter, LoD)
也叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。只和你的直接朋友通信,不要和陌生人说话,避免链式调用过深。
1.2 二、创建型模式
创建型模式关注对象的创建过程,它们隐藏了对象创建的细节,而不是使用new操作符直接实例化对象。这使得系统在创建什么对象、谁创建它、什么时候创建它等方面拥有更大的灵活性。
1.2.1 2.1 单例模式(Singleton)
1.2.1.1 意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1.2.1.2 适用场景
-
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
-
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
-
常见的应用:日志管理器、配置管理器、数据库连接池、线程池等。
1.2.1.3 C++ 实现
cpp
// C++11 线程安全单例(推荐使用)
#include <mutex>
#include <memory>
class Singleton {
private:
// 私有构造函数,禁止外部实例化
Singleton() = default;
// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 静态实例,使用智能指针管理内存
static std::unique_ptr<Singleton> instance;
static std::once_flag init_flag;
public:
// 获取全局唯一实例
static Singleton* getInstance() {
// call_once 保证只执行一次初始化,线程安全
std::call_once(init_flag, []() {
instance = std::make_unique<Singleton>();
});
return instance.get();
}
// 示例业务方法
void doSomething() {
// 业务逻辑
}
};
// 静态成员初始化
std::unique_ptr<Singleton> Singleton::instance = nullptr;
std::once_flag Singleton::init_flag;
1.2.1.4 优缺点
优点:
-
保证类只有一个实例,节省内存资源。
-
提供全局访问点,方便全局调用。
-
对唯一实例的受控访问。
缺点:
-
没有抽象层,扩展困难。
-
职责过重,一定程度上违背了单一职责原则。
-
滥用单例可能导致耦合过高,不利于单元测试。
1.2.2 2.2 工厂方法模式(Factory Method)
1.2.2.1 意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
1.2.2.2 适用场景
-
当一个类不知道它所必须创建的对象的类的时候。
-
当一个类希望由它的子类来指定它所创建的对象的时候。
-
当你将创建对象的逻辑封装起来,将客户端与具体类的实现解耦的时候。
1.2.2.3 C++ 实现
cpp
#include <iostream>
#include <memory>
#include <string>
// 产品接口
class Product {
public:
virtual ~Product() = default;
virtual std::string operation() const = 0;
};
// 具体产品A
class ConcreteProductA : public Product {
public:
std::string operation() const override {
return "Result of ConcreteProductA";
}
};
// 具体产品B
class ConcreteProductB : public Product {
public:
std::string operation() const override {
return "Result of ConcreteProductB";
}
};
// 创建者接口
class Creator {
public:
virtual ~Creator() = default;
virtual std::unique_ptr<Product> factoryMethod() const = 0;
// 业务逻辑,使用产品
std::string someOperation() const {
auto product = factoryMethod();
return "Creator: The same creator's code has just worked with " + product->operation();
}
};
// 具体创建者A
class ConcreteCreatorA : public Creator {
public:
std::unique_ptr<Product> factoryMethod() const override {
return std::make_unique<ConcreteProductA>();
}
};
// 具体创建者B
class ConcreteCreatorB : public Creator {
public:
std::unique_ptr<Product> factoryMethod() const override {
return std::make_unique<ConcreteProductB>();
}
};
int main() {
std::unique_ptr<Creator> creatorA = std::make_unique<ConcreteCreatorA>();
std::cout << creatorA->someOperation() << std::endl;
std::unique_ptr<Creator> creatorB = std::make_unique<ConcreteCreatorB>();
std::cout << creatorB->someOperation() << std::endl;
return 0;
}
1.2.2.4 优缺点
优点:
-
符合开闭原则,添加新产品时不需要修改原有代码。
-
符合单一职责原则,创建逻辑和业务逻辑分离。
-
客户端只依赖抽象,不依赖具体实现。
缺点:
- 每增加一个产品,就需要增加一个对应的创建者类,导致类的数量成倍增加。
1.2.3 2.3 抽象工厂模式(Abstract Factory)
1.2.3.1 意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
1.2.3.2 适用场景
-
当一个系统要独立于它的产品的创建、组合和表示时。
-
当一个系统要由多个产品系列中的一个来配置时。
-
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
-
常见应用:跨平台 UI 组件库(Windows 按钮 + Windows 输入框,Mac 按钮 + Mac 输入框)、数据库驱动族等。
1.2.3.3 C++ 实现
cpp
#include <iostream>
#include <memory>
#include <string>
// 抽象产品A:按钮
class Button {
public:
virtual ~Button() = default;
virtual void paint() const = 0;
};
// 具体产品A1:Windows按钮
class WinButton : public Button {
public:
void paint() const override {
std::cout << "Windows Button" << std::endl;
}
};
// 具体产品A2:Mac按钮
class MacButton : public Button {
public:
void paint() const override {
std::cout << "Mac Button" << std::endl;
}
};
// 抽象产品B:复选框
class Checkbox {
public:
virtual ~Checkbox() = default;
virtual void paint() const = 0;
};
// 具体产品B1:Windows复选框
class WinCheckbox : public Checkbox {
public:
void paint() const override {
std::cout << "Windows Checkbox" << std::endl;
}
};
// 具体产品B2:Mac复选框
class MacCheckbox : public Checkbox {
public:
void paint() const override {
std::cout << "Mac Checkbox" << std::endl;
}
};
// 抽象工厂
class GUIFactory {
public:
virtual ~GUIFactory() = default;
virtual std::unique_ptr<Button> createButton() const = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() const = 0;
};
// 具体工厂1:Windows工厂
class WinFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<WinButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<WinCheckbox>();
}
};
// 具体工厂2:Mac工厂
class MacFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<MacButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<MacCheckbox>();
}
};
// 客户端代码
class Application {
private:
std::unique_ptr<GUIFactory> factory;
std::unique_ptr<Button> button;
std::unique_ptr<Checkbox> checkbox;
public:
Application(std::unique_ptr<GUIFactory> f) : factory(std::move(f)) {
button = factory->createButton();
checkbox = factory->createCheckbox();
}
void paint() {
button->paint();
checkbox->paint();
}
};
int main() {
// 根据系统选择不同的工厂
std::unique_ptr<GUIFactory> factory;
#ifdef _WIN32
factory = std::make_unique<WinFactory>();
#else
factory = std::make_unique<MacFactory>();
#endif
Application app(std::move(factory));
app.paint();
return 0;
}
1.2.3.4 优缺点
优点:
-
保证了同一工厂创建的产品之间的兼容性。
-
避免客户端与具体产品的耦合,客户端只依赖抽象。
-
符合开闭原则,添加新的产品族很容易。
缺点:
-
扩展产品等级结构很困难,添加一个新产品,就需要修改抽象工厂和所有的具体工厂。
-
类的数量会急剧增加,增加了系统的复杂度。
1.2.4 2.4 建造者模式(Builder)
1.2.4.1 意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1.2.4.2 适用场景
-
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
-
当构造过程必须允许被构造的对象有不同的表示时。
-
常见应用:构建复杂的配置对象、HTTP 请求、文档对象等。
1.2.4.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// 产品:复杂对象
class House {
public:
std::vector<std::string> parts;
void show() const {
std::cout << "House parts: ";
for (const auto& part : parts) {
std::cout << part << " ";
}
std::cout << std::endl;
}
};
// 建造者接口
class Builder {
public:
virtual ~Builder() = default;
virtual void buildWalls() = 0;
virtual void buildDoor() = 0;
virtual void buildWindow() = 0;
virtual void buildRoof() = 0;
virtual std::unique_ptr<House> getResult() = 0;
};
// 具体建造者:普通房子
class ConcreteBuilder1 : public Builder {
private:
std::unique_ptr<House> house;
public:
ConcreteBuilder1() {
reset();
}
void reset() {
house = std::make_unique<House>();
}
void buildWalls() override {
house->parts.push_back("普通墙");
}
void buildDoor() override {
house->parts.push_back("普通门");
}
void buildWindow() override {
house->parts.push_back("普通窗");
}
void buildRoof() override {
house->parts.push_back("普通屋顶");
}
std::unique_ptr<House> getResult() override {
auto result = std::move(house);
reset();
return result;
}
};
// 具体建造者:别墅
class ConcreteBuilder2 : public Builder {
private:
std::unique_ptr<House> house;
public:
ConcreteBuilder2() {
reset();
}
void reset() {
house = std::make_unique<House>();
}
void buildWalls() override {
house->parts.push_back("豪华墙");
}
void buildDoor() override {
house->parts.push_back("智能门");
}
void buildWindow() override {
house->parts.push_back("落地窗");
}
void buildRoof() override {
house->parts.push_back("花园屋顶");
}
std::unique_ptr<House> getResult() override {
auto result = std::move(house);
reset();
return result;
}
};
// 指挥者:控制构建流程
class Director {
private:
Builder* builder;
public:
void setBuilder(Builder* b) {
builder = b;
}
// 构建最小产品
void buildMinimalViableHouse() {
builder->buildWalls();
builder->buildDoor();
}
// 构建完整产品
void buildFullFeaturedHouse() {
builder->buildWalls();
builder->buildDoor();
builder->buildWindow();
builder->buildRoof();
}
};
int main() {
Director director;
ConcreteBuilder1 builder1;
// 构建普通房子
director.setBuilder(&builder1);
director.buildFullFeaturedHouse();
auto house1 = builder1.getResult();
house1->show();
// 构建别墅
ConcreteBuilder2 builder2;
director.setBuilder(&builder2);
director.buildFullFeaturedHouse();
auto house2 = builder2.getResult();
house2->show();
return 0;
}
1.2.4.4 优缺点
优点:
-
可以分步构建复杂对象,或者延迟构建步骤。
-
可以使用相同的构建过程创建不同的表示。
-
隔离了复杂对象的构建和表示,符合单一职责原则。
缺点:
-
增加了很多额外的类,增加了系统的复杂度。
-
要求产品的组成部分必须是相似的,否则无法使用。
1.2.5 2.5 原型模式(Prototype)
1.2.5.1 意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
就像孙悟空的毫毛,拔一根就能复制出一个一模一样的自己。
1.2.5.2 适用场景
-
当一个系统应该独立于它的产品创建、构成和表示时。
-
当要实例化的类是在运行时刻指定时。
-
当创建对象的成本很高,而我们需要大量相似对象的时候。
1.2.5.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
#include <unordered_map>
// 原型接口
class Prototype {
public:
virtual ~Prototype() = default;
virtual std::unique_ptr<Prototype> clone() const = 0;
};
// 具体原型
class ConcretePrototype1 : public Prototype {
private:
int field1;
std::string field2;
public:
ConcretePrototype1(int f1, std::string f2) : field1(f1), field2(std::move(f2)) {}
std::unique_ptr<Prototype> clone() const override {
// 拷贝构造函数实现深拷贝
return std::make_unique<ConcretePrototype1>(*this);
}
void show() const {
std::cout << "ConcretePrototype1: " << field1 << ", " << field2 << std::endl;
}
};
// 原型注册表
class PrototypeFactory {
private:
std::unordered_map<std::string, std::unique_ptr<Prototype>> prototypes;
public:
PrototypeFactory() {
// 注册原型
prototypes["type1"] = std::make_unique<ConcretePrototype1>(100, "default");
}
void registerPrototype(const std::string& name, std::unique_ptr<Prototype> proto) {
prototypes[name] = std::move(proto);
}
std::unique_ptr<Prototype> createPrototype(const std::string& name) {
if (prototypes.find(name) != prototypes.end()) {
return prototypes[name]->clone();
}
return nullptr;
}
};
int main() {
PrototypeFactory factory;
// 从原型克隆新对象
auto p1 = factory.createPrototype("type1");
auto p2 = factory.createPrototype("type1");
// 两个对象是独立的
dynamic_cast<ConcretePrototype1*>(p1.get())->show();
dynamic_cast<ConcretePrototype1*>(p2.get())->show();
return 0;
}
1.2.5.4 优缺点
优点:
-
可以在运行时动态添加和删除原型。
-
比使用 new 创建对象更高效,特别是对象创建成本很高的时候。
-
可以通过克隆原型来快速创建相似对象,减少重复代码。
缺点:
-
实现深拷贝的时候可能比较复杂,特别是对象包含循环引用的时候。
-
每个原型类都需要实现克隆方法,有一定的代码量。
1.3 三、结构型模式
结构型模式描述如何将类或对象按某种布局组成更大的结构。它们分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合 / 聚合来组合对象。
1.3.1 3.1 适配器模式(Adapter)
1.3.1.1 意图
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
就像电源适配器,把不同国家的插座接口转换成你手机充电器能用的接口。
1.3.1.2 适用场景
-
你想使用一个已经存在的类,而它的接口不符合你的需求。
-
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作。
-
你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。
-
常见应用:集成第三方库、旧系统升级兼容、协议转换等。
1.3.1.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 目标接口:客户端期望的接口
class Target {
public:
virtual ~Target() = default;
virtual void request() const {
std::cout << "Target: Default behavior." << std::endl;
}
};
// 被适配者:已经存在的、接口不兼容的类
class Adaptee {
public:
void specificRequest() const {
std::cout << "Adaptee: Specific behavior." << std::endl;
}
};
// 适配器:将Adaptee的接口适配成Target的接口
class Adapter : public Target {
private:
std::unique_ptr<Adaptee> adaptee;
public:
Adapter(std::unique_ptr<Adaptee> a) : adaptee(std::move(a)) {}
void request() const override {
// 调用被适配者的方法,完成接口转换
adaptee->specificRequest();
}
};
// 客户端代码
void clientCode(const Target* target) {
target->request();
}
int main() {
// 正常的目标对象
std::cout << "Client: I can work just fine with the Target objects:" << std::endl;
auto target = std::make_unique<Target>();
clientCode(target.get());
// 被适配者,接口不兼容
std::cout << "\nClient: The Adaptee class has a weird interface. See, I don't understand it:" << std::endl;
auto adaptee = std::make_unique<Adaptee>();
adaptee->specificRequest();
// 使用适配器,就可以兼容了
std::cout << "\nClient: But I can work with it via the Adapter:" << std::endl;
auto adapter = std::make_unique<Adapter>(std::move(adaptee));
clientCode(adapter.get());
return 0;
}
1.3.1.4 优缺点
优点:
-
可以让两个不兼容的接口一起工作。
-
提高了类的复用性,不需要修改原有代码。
-
符合单一职责原则,接口转换的逻辑和业务逻辑分离。
-
符合开闭原则,可以添加新的适配器。
缺点:
- 过多的使用适配器会让系统变得混乱,不容易看清整体的结构。
1.3.2 3.2 桥接模式(Bridge)
1.3.2.1 意图
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
1.3.2.2 适用场景
-
你不希望在抽象和它的实现部分之间有一个固定的绑定关系。
-
类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。
-
你对一个抽象的实现部分的修改应对客户不产生影响。
-
你有很多类,你的类层次需要分解为两个独立的维度。
-
常见应用:图形渲染 API 与平台实现分离、消息通知与发送渠道分离等。
1.3.2.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 实现部分:绘图API
class DrawingAPI {
public:
virtual ~DrawingAPI() = default;
virtual void drawCircle(double x, double y, double radius) = 0;
virtual void drawRectangle(double x, double y, double w, double h) = 0;
};
// 具体实现1:Windows API
class DrawingAPI1 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) override {
std::cout << "API1.circle at " << x << ":" << y << " radius " << radius << std::endl;
}
void drawRectangle(double x, double y, double w, double h) override {
std::cout << "API1.rectangle at " << x << ":" << y << " " << w << "x" << h << std::endl;
}
};
// 具体实现2:Mac API
class DrawingAPI2 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) override {
std::cout << "API2.circle at " << x << ":" << y << " radius " << radius << std::endl;
}
void drawRectangle(double x, double y, double w, double h) override {
std::cout << "API2.rectangle at " << x << ":" << y << " " << w << "x" << h << std::endl;
}
};
// 抽象部分:形状
class Shape {
protected:
std::unique_ptr<DrawingAPI> drawingAPI;
public:
Shape(std::unique_ptr<DrawingAPI> api) : drawingAPI(std::move(api)) {}
virtual ~Shape() = default;
virtual void draw() = 0;
virtual void resize(double factor) = 0;
};
// 具体抽象:圆形
class CircleShape : public Shape {
private:
double x, y, radius;
public:
CircleShape(double x, double y, double r, std::unique_ptr<DrawingAPI> api)
: Shape(std::move(api)), x(x), y(y), radius(r) {}
void draw() override {
drawingAPI->drawCircle(x, y, radius);
}
void resize(double factor) override {
radius *= factor;
}
};
// 具体抽象:矩形
class RectangleShape : public Shape {
private:
double x, y, w, h;
public:
RectangleShape(double x, double y, double w, double h, std::unique_ptr<DrawingAPI> api)
: Shape(std::move(api)), x(x), y(y), w(w), h(h) {}
void draw() override {
drawingAPI->drawRectangle(x, y, w, h);
}
void resize(double factor) override {
w *= factor;
h *= factor;
}
};
int main() {
// 我们可以独立地变化形状和绘图API
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<CircleShape>(1, 2, 3, std::make_unique<DrawingAPI1>()));
shapes.push_back(std::make_unique<CircleShape>(5, 7, 11, std::make_unique<DrawingAPI2>()));
shapes.push_back(std::make_unique<RectangleShape>(2, 3, 4, 5, std::make_unique<DrawingAPI1>()));
for (const auto& shape : shapes) {
shape->draw();
}
return 0;
}
1.3.2.4 优缺点
优点:
-
分离了抽象和实现,解耦了两个维度的变化。
-
提高了系统的可扩展性,两个维度都可以独立扩展。
-
可以在运行时切换实现。
-
符合开闭原则,添加新的抽象或实现都很容易。
缺点:
-
增加了系统的复杂度,需要引入额外的层。
-
客户端需要了解不同的实现,才能正确使用。
1.3.3 3.3 组合模式(Composite)
1.3.3.1 意图
将对象组合成树形结构以表示 "部分 - 整体" 的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。
1.3.3.2 适用场景
-
你想表示对象的部分 - 整体层次结构。
-
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
-
常见应用:文件系统、菜单树、DOM 树、图形场景图等。
1.3.3.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
// 组件:统一叶子和容器的接口
class Component {
protected:
Component* parent_;
std::string name_;
public:
Component(const std::string& name) : name_(name), parent_(nullptr) {}
virtual ~Component() = default;
void setParent(Component* parent) {
parent_ = parent;
}
Component* getParent() const {
return parent_;
}
virtual void add(Component* component) {}
virtual void remove(Component* component) {}
virtual bool isComposite() const {
return false;
}
virtual std::string operation() const = 0;
};
// 叶子:叶子节点,没有子节点
class Leaf : public Component {
public:
Leaf(const std::string& name) : Component(name) {}
std::string operation() const override {
return name_;
}
};
// 组合:容器节点,可以包含子节点
class Composite : public Component {
protected:
std::vector<std::unique_ptr<Component>> children_;
public:
Composite(const std::string& name) : Component(name) {}
void add(Component* component) override {
component->setParent(this);
children_.push_back(std::unique_ptr<Component>(component));
}
void remove(Component* component) override {
auto it = std::find_if(children_.begin(), children_.end(),
[&](const std::unique_ptr<Component>& c) {
return c.get() == component;
});
if (it != children_.end()) {
(*it)->setParent(nullptr);
children_.erase(it);
}
}
bool isComposite() const override {
return true;
}
std::string operation() const override {
std::string result;
for (const auto& child : children_) {
result += child->operation();
if (child != children_.back()) {
result += "+";
}
}
return name_ + "(" + result + ")";
}
};
// 客户端代码
void clientCode(Component* component) {
std::cout << "Result: " << component->operation() << std::endl;
}
int main() {
// 简单的叶子节点
std::unique_ptr<Component> simple = std::make_unique<Leaf>("Leaf");
clientCode(simple.get());
// 复杂的组合
std::unique_ptr<Component> tree = std::make_unique<Composite>("Root");
std::unique_ptr<Component> branch1 = std::make_unique<Composite>("Branch1");
std::unique_ptr<Component> branch2 = std::make_unique<Composite>("Branch2");
branch1->add(new Leaf("Leaf A"));
branch1->add(new Leaf("Leaf B"));
branch2->add(new Leaf("Leaf C"));
branch2->add(new Leaf("Leaf D"));
tree->add(branch1.release());
tree->add(branch2.release());
clientCode(tree.get());
return 0;
}
1.3.3.4 优缺点
优点:
-
可以很方便地定义层次结构。
-
客户端可以统一处理单个对象和组合对象,简化了客户端代码。
-
符合开闭原则,添加新的叶子或容器都很容易。
缺点:
-
使得设计变得过于通用,有时候会限制某些功能。
-
可能会让层次结构变得过于复杂。
1.3.4 3.4 装饰器模式(Decorator)
1.3.4.1 意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。
1.3.4.2 适用场景
-
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
-
处理那些可以撤销的职责。
-
当不能采用生成子类的方法进行扩充时。
-
常见应用:IO 流包装、UI 效果叠加、中间件等。
1.3.4.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 组件接口
class Coffee {
public:
virtual ~Coffee() = default;
virtual double cost() const = 0;
virtual std::string getDescription() const = 0;
};
// 具体组件:基础咖啡
class SimpleCoffee : public Coffee {
public:
double cost() const override {
return 1.0;
}
std::string getDescription() const override {
return "Simple Coffee";
}
};
// 装饰器基类
class CoffeeDecorator : public Coffee {
protected:
std::unique_ptr<Coffee> coffee_;
public:
CoffeeDecorator(std::unique_ptr<Coffee> c) : coffee_(std::move(c)) {}
};
// 具体装饰器:加牛奶
class MilkDecorator : public CoffeeDecorator {
public:
MilkDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
double cost() const override {
return coffee_->cost() + 0.5;
}
std::string getDescription() const override {
return coffee_->getDescription() + ", Milk";
}
};
// 具体装饰器:加糖
class SugarDecorator : public CoffeeDecorator {
public:
SugarDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
double cost() const override {
return coffee_->cost() + 0.2;
}
std::string getDescription() const override {
return coffee_->getDescription() + ", Sugar";
}
};
// 具体装饰器:加奶泡
class WhipDecorator : public CoffeeDecorator {
public:
WhipDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
double cost() const override {
return coffee_->cost() + 0.7;
}
std::string getDescription() const override {
return coffee_->getDescription() + ", Whip";
}
};
int main() {
// 基础咖啡
std::unique_ptr<Coffee> coffee = std::make_unique<SimpleCoffee>();
std::cout << coffee->getDescription() << " $" << coffee->cost() << std::endl;
// 加牛奶
coffee = std::make_unique<MilkDecorator>(std::move(coffee));
std::cout << coffee->getDescription() << " $" << coffee->cost() << std::endl;
// 加糖
coffee = std::make_unique<SugarDecorator>(std::move(coffee));
std::cout << coffee->getDescription() << " $" << coffee->cost() << std::endl;
// 加奶泡
coffee = std::make_unique<WhipDecorator>(std::move(coffee));
std::cout << coffee->getDescription() << " $" << coffee->cost() << std::endl;
return 0;
}
1.3.4.4 优缺点
优点:
-
比继承更加灵活,可以动态地添加或删除功能。
-
可以用多个装饰器来装饰一个对象,实现功能的叠加。
-
符合开闭原则,添加新的装饰器不需要修改原有代码。
-
符合单一职责原则,每个装饰器只负责一个功能。
缺点:
-
会产生很多小的装饰类,增加了系统的复杂度。
-
客户端代码可能会依赖于具体的装饰器顺序。
1.3.5 3.5 外观模式(Facade)
1.3.5.1 意图
为子系统中的一组接口提供一个一致的界面,Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
1.3.5.2 适用场景
-
你要为一个复杂子系统提供一个简单的接口。
-
客户程序与抽象类的实现部分之间存在着很大的依赖性。
-
当你需要构建一个层次结构的子系统时,使用 facade 模式定义子系统中每层的入口点。
-
常见应用:简化复杂系统的调用、SDK 封装、系统启动流程等。
1.3.5.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 子系统1:视频文件
class VideoFile {
private:
std::string filename_;
public:
VideoFile(const std::string& filename) : filename_(filename) {}
std::string getFilename() const { return filename_; }
};
// 子系统2:音频提取器
class AudioExtractor {
public:
void extractAudio(const VideoFile* file) {
std::cout << "Extracting audio from " << file->getFilename() << std::endl;
}
};
// 子系统3:视频压缩器
class VideoCompressor {
public:
void compress(const VideoFile* file) {
std::cout << "Compressing " << file->getFilename() << std::endl;
}
};
// 子系统4:音频混合器
class AudioMixer {
public:
void mix() {
std::cout << "Mixing audio..." << std::endl;
}
};
// 外观:视频转换工具
class VideoConverterFacade {
public:
void convert(const std::string& filename) {
// 封装了复杂的子系统调用流程
VideoFile file(filename);
AudioExtractor extractor;
VideoCompressor compressor;
AudioMixer mixer;
extractor.extractAudio(&file);
mixer.mix();
compressor.compress(&file);
std::cout << "Conversion completed!" << std::endl;
}
};
int main() {
// 客户端只需要调用外观的简单接口,不需要了解子系统的复杂细节
VideoConverterFacade converter;
converter.convert("movie.mp4");
return 0;
}
1.3.5.4 优缺点
优点:
-
简化了客户端的使用,客户端只需要调用一个简单的接口。
-
减少了客户端与子系统之间的耦合。
-
可以很方便地对系统进行分层。
缺点:
-
外观类可能会变成一个 "上帝类",耦合了所有的子系统。
-
可能会限制客户端对子系统的直接访问。
1.3.6 3.6 享元模式(Flyweight)
1.3.6.1 意图
运用共享技术有效地支持大量细粒度的对象。
1.3.6.2 适用场景
-
一个应用程序使用了大量的对象。
-
完全由于使用大量的对象,造成很大的存储开销。
-
对象的大多数状态都可变为外部状态。
-
如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
-
常见应用:文本编辑器中的字符对象、游戏中的粒子对象、连接池等。
1.3.6.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
// 享元:包含内部状态(可以共享的)
class Flyweight {
private:
// 内部状态:共享的,不随环境变化
std::string intrinsicState_;
public:
Flyweight(const std::string& state) : intrinsicState_(state) {}
// 操作:外部状态作为参数传入
void operation(const std::string& extrinsicState) const {
std::cout << "Flyweight: 内部状态=[" << intrinsicState_
<< "], 外部状态=[" << extrinsicState << "]" << std::endl;
}
};
// 享元工厂:创建和管理享元对象
class FlyweightFactory {
private:
std::unordered_map<std::string, std::unique_ptr<Flyweight>> flyweights_;
public:
Flyweight* getFlyweight(const std::string& key) {
// 如果已经存在,就返回已有的,否则创建新的
if (flyweights_.find(key) == flyweights_.end()) {
flyweights_[key] = std::make_unique<Flyweight>(key);
std::cout << "Creating new flyweight: " << key << std::endl;
} else {
std::cout << "Reusing existing flyweight: " << key << std::endl;
}
return flyweights_[key].get();
}
void listFlyweights() const {
std::cout << "Flyweight Factory: " << flyweights_.size() << " flyweights:" << std::endl;
for (const auto& pair : flyweights_) {
std::cout << pair.first << " ";
}
std::cout << std::endl;
}
};
int main() {
FlyweightFactory factory;
// 共享的享元对象
factory.getFlyweight("hello")->operation("first call");
factory.getFlyweight("world")->operation("second call");
factory.getFlyweight("hello")->operation("third call"); // 复用hello
factory.listFlyweights();
return 0;
}
1.3.6.4 优缺点
优点:
-
极大地减少了内存的使用,通过共享对象来节省资源。
-
可以处理大量的细粒度对象。
缺点:
-
为了共享对象,需要区分内部状态和外部状态,增加了系统的复杂度。
-
客户端代码可能会变得复杂,因为需要管理外部状态。
1.3.7 3.7 代理模式(Proxy)
1.3.7.1 意图
为其他对象提供一种代理以控制对这个对象的访问。
1.3.7.2 适用场景
-
远程代理:为一个对象在不同的地址空间提供局部代表。
-
虚拟代理:根据需要创建开销很大的对象。
-
保护代理:控制对原始对象的访问。
-
智能引用:在访问对象时执行一些附加操作。
-
常见应用:懒加载、远程调用、权限控制、缓存等。
1.3.7.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 主题接口
class Subject {
public:
virtual ~Subject() = default;
virtual void request() const = 0;
};
// 真实主题:真正的业务对象
class RealSubject : public Subject {
public:
void request() const override {
std::cout << "RealSubject: Handling request." << std::endl;
}
};
// 代理
class Proxy : public Subject {
private:
std::unique_ptr<RealSubject> realSubject_;
// 检查访问权限
bool checkAccess() const {
std::cout << "Proxy: Checking access prior to firing a real request." << std::endl;
return true;
}
// 日志记录
void logAccess() const {
std::cout << "Proxy: Logging the time of request." << std::endl;
}
public:
Proxy(std::unique_ptr<RealSubject> real) : realSubject_(std::move(real)) {}
void request() const override {
if (checkAccess()) {
realSubject_->request();
logAccess();
}
}
};
// 客户端代码
void clientCode(const Subject* subject) {
subject->request();
}
int main() {
std::cout << "Client: Executing the client code with a real subject:" << std::endl;
auto realSubject = std::make_unique<RealSubject>();
clientCode(realSubject.get());
std::cout << "\nClient: Executing the same client code with a proxy:" << std::endl;
auto proxy = std::make_unique<Proxy>(std::move(realSubject));
clientCode(proxy.get());
return 0;
}
1.3.7.4 优缺点
优点:
-
可以控制对真实对象的访问,在访问前后做一些额外的操作。
-
可以实现懒加载,在需要的时候才创建真实对象。
-
代理可以独立于真实对象变化,不需要修改真实对象。
缺点:
-
增加了系统的复杂度,引入了额外的代理类。
-
可能会增加请求的响应时间。
1.4 四、行为型模式
行为型模式关注对象之间的通信,以及职责的分配。它们描述了对象或类之间的交互模式,以及如何将行为分布在这些对象之间。
1.4.1 4.1 模板方法模式(Template Method)
1.4.1.1 意图
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
1.4.1.2 适用场景
-
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
-
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
-
控制子类扩展。
-
常见应用:框架中的固定流程、测试框架、数据处理流程等。
1.4.1.3 C++ 实现
cpp
#include <iostream>
#include <string>
// 抽象类:定义算法骨架
class AbstractClass {
public:
// 模板方法:定义算法的骨架
void templateMethod() const {
baseOperation1();
requiredOperation1();
baseOperation2();
hookOperation1();
requiredOperation2();
}
protected:
// 基础操作:已经有默认实现
void baseOperation1() const {
std::cout << "AbstractClass: Base operation 1." << std::endl;
}
void baseOperation2() const {
std::cout << "AbstractClass: Base operation 2." << std::endl;
}
// 必须实现的操作:子类必须重写
virtual void requiredOperation1() const = 0;
virtual void requiredOperation2() const = 0;
// 钩子操作:可选的,子类可以重写
virtual void hookOperation1() const {}
};
// 具体类1
class ConcreteClass1 : public AbstractClass {
protected:
void requiredOperation1() const override {
std::cout << "ConcreteClass1: Implemented required operation 1." << std::endl;
}
void requiredOperation2() const override {
std::cout << "ConcreteClass1: Implemented required operation 2." << std::endl;
}
};
// 具体类2
class ConcreteClass2 : public AbstractClass {
protected:
void requiredOperation1() const override {
std::cout << "ConcreteClass2: Implemented required operation 1." << std::endl;
}
void requiredOperation2() const override {
std::cout << "ConcreteClass2: Implemented required operation 2." << std::endl;
}
// 重写钩子操作
void hookOperation1() const override {
std::cout << "ConcreteClass2: Overridden hook operation." << std::endl;
}
};
// 客户端代码
void clientCode(AbstractClass* class_) {
class_->templateMethod();
}
int main() {
std::cout << "Same client code can work with different subclasses:" << std::endl;
ConcreteClass1 c1;
clientCode(&c1);
std::cout << "\n" << std::endl;
std::cout << "Same client code can work with different subclasses:" << std::endl;
ConcreteClass2 c2;
clientCode(&c2);
return 0;
}
1.4.1.4 优缺点
优点:
-
可以复用代码,提取公共的部分到父类。
-
可以很方便地扩展,子类只需要实现特定的步骤。
-
符合开闭原则,添加新的子类不需要修改原有代码。
缺点:
-
每个不同的实现都需要一个子类,导致类的数量增加。
-
父类的钩子方法会影响子类的行为,可能会导致耦合。
1.4.2 4.2 策略模式(Strategy)
1.4.2.1 意图
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
1.4.2.2 适用场景
-
多个类只有在算法或行为上稍有不同的场景。
-
算法需要自由切换的场景。
-
需要屏蔽算法规则的场景。
-
常见应用:排序算法、支付方式、AI 行为、压缩算法等。
1.4.2.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <memory>
// 策略接口
class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>& data) const = 0;
};
// 具体策略:冒泡排序
class BubbleSortStrategy : public SortStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Sorting using Bubble Sort..." << std::endl;
int n = data.size();
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (data[j] > data[j+1]) {
std::swap(data[j], data[j+1]);
}
}
}
}
};
// 具体策略:快速排序
class QuickSortStrategy : public SortStrategy {
private:
void quickSort(std::vector<int>& data, int low, int high) const {
if (low < high) {
int pi = partition(data, low, high);
quickSort(data, low, pi-1);
quickSort(data, pi+1, high);
}
}
int partition(std::vector<int>& data, int low, int high) const {
int pivot = data[high];
int i = low - 1;
for (int j = low; j <= high-1; j++) {
if (data[j] <= pivot) {
i++;
std::swap(data[i], data[j]);
}
}
std::swap(data[i+1], data[high]);
return i+1;
}
public:
void sort(std::vector<int>& data) const override {
std::cout << "Sorting using Quick Sort..." << std::endl;
quickSort(data, 0, data.size()-1);
}
};
// 上下文:使用策略的类
class Sorter {
private:
std::unique_ptr<SortStrategy> strategy_;
public:
Sorter(std::unique_ptr<SortStrategy> strategy) : strategy_(std::move(strategy)) {}
void setStrategy(std::unique_ptr<SortStrategy> strategy) {
strategy_ = std::move(strategy);
}
void sort(std::vector<int>& data) {
strategy_->sort(data);
}
};
int main() {
std::vector<int> data = {3, 1, 4, 1, 5, 9, 2, 6};
// 使用冒泡排序
Sorter sorter(std::make_unique<BubbleSortStrategy>());
sorter.sort(data);
for (int num : data) std::cout << num << " ";
std::cout << std::endl;
// 动态切换为快速排序
data = {3, 1, 4, 1, 5, 9, 2, 6};
sorter.setStrategy(std::make_unique<QuickSortStrategy>());
sorter.sort(data);
for (int num : data) std::cout << num << " ";
std::cout << std::endl;
return 0;
}
1.4.2.4 优缺点
优点:
-
可以动态地切换算法。
-
算法的实现和使用分离,符合单一职责原则。
-
符合开闭原则,添加新的算法很容易。
-
可以避免大量的条件判断语句。
缺点:
-
客户端需要了解不同的策略,才能选择合适的。
-
增加了类的数量,每个策略一个类。
1.4.3 4.3 状态模式(State)
1.4.3.1 意图
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
1.4.3.2 适用场景
-
一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
-
一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。
-
常见应用:有限状态机、游戏角色状态、订单状态流转等。
1.4.3.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 前向声明
class Context;
// 状态接口
class State {
public:
virtual ~State() = default;
virtual void handle1(Context* context) = 0;
virtual void handle2(Context* context) = 0;
};
// 上下文
class Context {
private:
std::unique_ptr<State> state_;
public:
Context(std::unique_ptr<State> state) : state_(std::move(state)) {}
void transitionTo(std::unique_ptr<State> state) {
std::cout << "Context: Transition to " << typeid(*state).name() << std::endl;
state_ = std::move(state);
}
void request1() {
state_->handle1(this);
}
void request2() {
state_->handle2(this);
}
};
// 具体状态A
class ConcreteStateA : public State {
public:
void handle1(Context* context) override;
void handle2(Context* context) override {
std::cout << "ConcreteStateA handles request2." << std::endl;
}
};
// 具体状态B
class ConcreteStateB : public State {
public:
void handle1(Context* context) override {
std::cout << "ConcreteStateB handles request1." << std::endl;
}
void handle2(Context* context) override {
std::cout << "ConcreteStateB handles request2." << std::endl;
context->transitionTo(std::make_unique<ConcreteStateA>());
}
};
void ConcreteStateA::handle1(Context* context) {
std::cout << "ConcreteStateA handles request1." << std::endl;
context->transitionTo(std::make_unique<ConcreteStateB>());
}
int main() {
Context context(std::make_unique<ConcreteStateA>());
context.request1(); // 会切换到状态B
context.request2(); // 会切换回状态A
context.request1(); // 又切换到状态B
return 0;
}
1.4.3.4 优缺点
优点:
-
将状态相关的行为局部化,将不同状态的行为分割开来。
-
消除了庞大的条件判断语句。
-
使得状态转换显式化,更容易管理状态流转。
-
符合开闭原则,添加新的状态很容易。
缺点:
-
增加了类的数量,每个状态一个类。
-
可能会导致状态之间的耦合,一个状态的改变可能会影响其他状态。
1.4.4 4.4 观察者模式(Observer)
1.4.4.1 意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
1.4.4.2 适用场景
-
当一个抽象模型有两个方面,其中一个方面依赖于另一方面。
-
当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
-
当一个对象必须通知其他对象,而它又不能假定其他对象是谁。
-
常见应用:事件系统、MVC 模式、发布 - 订阅模式、GUI 事件通知等。
1.4.4.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <list>
#include <memory>
// 前向声明
class Observer;
// 主题:被观察者
class Subject {
private:
std::list<Observer*> observers_;
int state_;
public:
int getState() const { return state_; }
void setState(int state) {
state_ = state;
notify();
}
void attach(Observer* observer);
void detach(Observer* observer);
void notify();
};
// 观察者接口
class Observer {
protected:
Subject* subject_;
int observerState_;
public:
Observer(Subject* subject) : subject_(subject) {}
virtual ~Observer() = default;
virtual void update() = 0;
};
void Subject::attach(Observer* observer) {
observers_.push_back(observer);
}
void Subject::detach(Observer* observer) {
observers_.remove(observer);
}
void Subject::notify() {
for (auto observer : observers_) {
observer->update();
}
}
// 具体观察者A
class ConcreteObserverA : public Observer {
public:
ConcreteObserverA(Subject* subject) : Observer(subject) {
subject->attach(this);
}
void update() override {
observerState_ = subject_->getState();
std::cout << "ObserverA: Reacted to the event. New state: " << observerState_ << std::endl;
}
};
// 具体观察者B
class ConcreteObserverB : public Observer {
public:
ConcreteObserverB(Subject* subject) : Observer(subject) {
subject->attach(this);
}
void update() override {
observerState_ = subject_->getState();
std::cout << "ObserverB: Reacted to the event. New state: " << observerState_ << std::endl;
}
};
int main() {
Subject subject;
auto observerA = std::make_unique<ConcreteObserverA>(&subject);
auto observerB = std::make_unique<ConcreteObserverB>(&subject);
std::cout << "Subject changes state to 10..." << std::endl;
subject.setState(10);
std::cout << "\nSubject changes state to 20..." << std::endl;
subject.setState(20);
// 移除一个观察者
subject.detach(observerB.get());
std::cout << "\nObserverB is detached. Subject changes state to 30..." << std::endl;
subject.setState(30);
return 0;
}
1.4.4.4 优缺点
优点:
-
实现了松耦合,主题和观察者之间没有紧密的依赖。
-
支持广播通信,主题可以通知所有的观察者。
-
符合开闭原则,添加新的观察者很容易。
缺点:
-
观察者不知道彼此的存在,可能会导致意外的更新。
-
如果观察者太多,通知的成本可能会很高。
-
如果有循环依赖,可能会导致无限循环。
1.4.5 4.5 备忘录模式(Memento)
1.4.5.1 意图
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
1.4.5.2 适用场景
-
必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
-
如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
-
常见应用:撤销 / 重做操作、游戏存档、事务回滚等。
1.4.5.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// 备忘录:保存原发器的状态
class Memento {
private:
// 只有原发器可以访问备忘录的状态
friend class Originator;
std::string state_;
Memento(const std::string& state) : state_(state) {}
std::string getState() const {
return state_;
}
public:
// 对外的接口,不暴露内部状态
std::string getName() const {
return "Memento(" + state_.substr(0, 10) + "...)";
}
};
// 原发器:需要保存状态的对象
class Originator {
private:
std::string state_;
public:
Originator(const std::string& state) : state_(state) {
std::cout << "Originator: Initial state is " << state_ << std::endl;
}
// 做一些业务操作,改变状态
void doSomething() {
std::cout << "Originator: Doing something important..." << std::endl;
state_ = "Random state " + std::to_string(rand());
std::cout << "Originator: And my state has changed to " << state_ << std::endl;
}
// 保存当前状态到备忘录
std::unique_ptr<Memento> save() const {
return std::unique_ptr<Memento>(new Memento(state_));
}
// 从备忘录恢复状态
void restore(const Memento* memento) {
state_ = memento->getState();
std::cout << "Originator: Restored state to: " << state_ << std::endl;
}
};
// 管理者:管理备忘录,不了解备忘录的内部
class Caretaker {
private:
std::vector<std::unique_ptr<Memento>> mementos_;
Originator* originator_;
public:
Caretaker(Originator* originator) : originator_(originator) {}
void backup() {
std::cout << "\nCaretaker: Saving Originator's state..." << std::endl;
mementos_.push_back(originator_->save());
}
void undo() {
if (mementos_.empty()) {
return;
}
auto memento = std::move(mementos_.back());
mementos_.pop_back();
std::cout << "Caretaker: Restoring state to: " << memento->getName() << std::endl;
originator_->restore(memento.get());
}
void showHistory() const {
std::cout << "Caretaker: Here's the list of mementos:" << std::endl;
for (const auto& m : mementos_) {
std::cout << m->getName() << std::endl;
}
}
};
int main() {
auto originator = std::make_unique<Originator>("Initial state");
auto caretaker = std::make_unique<Caretaker>(originator.get());
caretaker->backup();
originator->doSomething();
caretaker->backup();
originator->doSomething();
caretaker->backup();
originator->doSomething();
std::cout << "\nClient: Now, let's rollback!" << std::endl;
caretaker->undo();
std::cout << "\nClient: Once more!" << std::endl;
caretaker->undo();
std::cout << "\nHistory:" << std::endl;
caretaker->showHistory();
return 0;
}
1.4.5.4 优缺点
优点:
-
可以在不破坏封装的情况下保存对象的状态。
-
可以很方便地实现撤销 / 重做功能。
-
可以提供状态的快照,方便恢复。
缺点:
-
如果对象的状态很大,那么备忘录也会很大,消耗很多内存。
-
客户端需要跟踪备忘录的历史,管理起来比较复杂。
1.4.6 4.6 中介者模式(Mediator)
1.4.6.1 意图
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
1.4.6.2 适用场景
-
一组对象以定义良好但是复杂的方式进行通信,产生了相互依赖的结构。
-
你想定制一个分布在多个类中的行为,而又不想生成太多的子类。
-
常见应用:聊天室、UI 组件交互、机场调度系统等。
1.4.6.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 前向声明
class BaseComponent;
class Mediator;
// 中介者接口
class Mediator {
public:
virtual ~Mediator() = default;
virtual void notify(BaseComponent* sender, const std::string& event) = 0;
};
// 组件基类
class BaseComponent {
protected:
Mediator* mediator_;
public:
BaseComponent(Mediator* mediator = nullptr) : mediator_(mediator) {}
void setMediator(Mediator* mediator) {
mediator_ = mediator;
}
};
// 具体组件A
class ComponentA : public BaseComponent {
public:
void doA() {
std::cout << "Component A does A." << std::endl;
mediator_->notify(this, "A");
}
};
// 具体组件B
class ComponentB : public BaseComponent {
public:
void doB() {
std::cout << "Component B does B." << std::endl;
mediator_->notify(this, "B");
}
};
// 具体中介者
class ConcreteMediator : public Mediator {
private:
ComponentA* componentA_;
ComponentB* componentB_;
public:
ConcreteMediator(ComponentA* a, ComponentB* b)
: componentA_(a), componentB_(b) {
componentA_->setMediator(this);
componentB_->setMediator(this);
}
void notify(BaseComponent* sender, const std::string& event) override {
if (event == "A") {
std::cout << "Mediator reacts on A and triggers B." << std::endl;
componentB_->doB();
}
if (event == "B") {
std::cout << "Mediator reacts on B and triggers A." << std::endl;
componentA_->doA();
}
}
};
int main() {
ComponentA* c1 = new ComponentA();
ComponentB* c2 = new ComponentB();
ConcreteMediator* mediator = new ConcreteMediator(c1, c2);
std::cout << "Client triggers operation A." << std::endl;
c1->doA();
std::cout << "\nClient triggers operation B." << std::endl;
c2->doB();
delete c1;
delete c2;
delete mediator;
return 0;
}
1.4.6.4 优缺点
优点:
-
减少了组件之间的耦合,使得组件之间不需要直接通信。
-
将对象之间的交互集中到中介者中,简化了系统的结构。
-
符合开闭原则,添加新的组件很容易。
-
符合单一职责原则,组件只负责自己的业务,交互交给中介者。
缺点:
-
中介者可能会变得非常复杂,成为一个 "上帝类",耦合了所有的组件。
-
中介者本身的复杂度可能会很高。
1.4.7 4.7 命令模式(Command)
1.4.7.1 意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
1.4.7.2 适用场景
-
抽象出待执行的动作以参数化某对象。
-
在不同的时刻指定、排列和执行请求。
-
支持取消操作。
-
支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。
-
常见应用:撤销 / 重做、任务队列、远程调用、事务等。
1.4.7.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
#include <stack>
// 命令接口
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
};
// 简单命令:只做一个简单的操作
class SimpleCommand : public Command {
private:
std::string payload_;
public:
SimpleCommand(const std::string& payload) : payload_(payload) {}
void execute() override {
std::cout << "SimpleCommand: Printing " << payload_ << std::endl;
}
void undo() override {
std::cout << "SimpleCommand: Undo printing " << payload_ << std::endl;
}
};
// 复杂命令:操作接收者
class ComplexCommand : public Command {
private:
class Receiver* receiver_;
std::string a_;
std::string b_;
public:
ComplexCommand(Receiver* receiver, const std::string& a, const std::string& b);
void execute() override;
void undo() override;
};
// 接收者:真正执行命令的对象
class Receiver {
public:
void doSomething(const std::string& a) {
std::cout << "Receiver: Working on " << a << "." << std::endl;
}
void doSomethingElse(const std::string& b) {
std::cout << "Receiver: Also working on " << b << "." << std::endl;
}
void undoSomething(const std::string& a) {
std::cout << "Receiver: Undoing " << a << "." << std::endl;
}
void undoSomethingElse(const std::string& b) {
std::cout << "Receiver: Undoing " << b << "." << std::endl;
}
};
ComplexCommand::ComplexCommand(Receiver* receiver, const std::string& a, const std::string& b)
: receiver_(receiver), a_(a), b_(b) {}
void ComplexCommand::execute() {
receiver_->doSomething(a_);
receiver_->doSomethingElse(b_);
}
void ComplexCommand::undo() {
receiver_->undoSomethingElse(b_);
receiver_->undoSomething(a_);
}
// 调用者:发送命令的对象
class Invoker {
private:
std::unique_ptr<Command> onStart_;
std::unique_ptr<Command> onFinish_;
std::stack<std::unique_ptr<Command>> history_;
public:
void setOnStart(std::unique_ptr<Command> command) {
onStart_ = std::move(command);
}
void setOnFinish(std::unique_ptr<Command> command) {
onFinish_ = std::move(command);
}
void doSomethingImportant() {
std::cout << "Invoker: Does anybody want something done before I begin?" << std::endl;
if (onStart_) {
onStart_->execute();
history_.push(std::move(onStart_));
}
std::cout << "Invoker: ...doing something really important..." << std::endl;
std::cout << "Invoker: Does anybody want something done after I finish?" << std::endl;
if (onFinish_) {
onFinish_->execute();
history_.push(std::move(onFinish_));
}
}
void undo() {
if (history_.empty()) {
return;
}
auto command = std::move(history_.top());
history_.pop();
command->undo();
}
};
int main() {
Invoker invoker;
invoker.setOnStart(std::make_unique<SimpleCommand>("Say Hi!"));
Receiver* receiver = new Receiver();
invoker.setOnFinish(std::make_unique<ComplexCommand>(receiver, "Send email", "Save report"));
invoker.doSomethingImportant();
std::cout << "\nUndo last operation..." << std::endl;
invoker.undo();
std::cout << "\nUndo previous operation..." << std::endl;
invoker.undo();
delete receiver;
return 0;
}
1.4.7.4 优缺点
优点:
-
将请求的发送者和接收者解耦,发送者不需要知道接收者是谁。
-
可以很容易地实现撤销 / 重做功能。
-
可以将命令排队,或者记录日志。
-
符合开闭原则,添加新的命令很容易。
缺点:
- 会增加很多命令类,每个命令一个类,增加了系统的复杂度。
1.4.8 4.8 访问者模式(Visitor)
1.4.8.1 意图
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
1.4.8.2 适用场景
-
一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
-
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作 "污染" 这些对象的类。
-
常见应用:编译器 AST 遍历、报表生成、数据结构的操作扩展等。
1.4.8.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// 前向声明
class ConcreteElementA;
class ConcreteElementB;
// 访问者接口
class Visitor {
public:
virtual ~Visitor() = default;
virtual void visitConcreteElementA(const ConcreteElementA* element) = 0;
virtual void visitConcreteElementB(const ConcreteElementB* element) = 0;
};
// 元素接口
class Element {
public:
virtual ~Element() = default;
virtual void accept(Visitor* visitor) const = 0;
};
// 具体元素A
class ConcreteElementA : public Element {
public:
void accept(Visitor* visitor) const override {
visitor->visitConcreteElementA(this);
}
std::string exclusiveMethodOfA() const {
return "A";
}
};
// 具体元素B
class ConcreteElementB : public Element {
public:
void accept(Visitor* visitor) const override {
visitor->visitConcreteElementB(this);
}
std::string exclusiveMethodOfB() const {
return "B";
}
};
// 具体访问者1:打印
class ConcreteVisitor1 : public Visitor {
public:
void visitConcreteElementA(const ConcreteElementA* element) override {
std::cout << "ConcreteVisitor1: " << element->exclusiveMethodOfA() << std::endl;
}
void visitConcreteElementB(const ConcreteElementB* element) override {
std::cout << "ConcreteVisitor1: " << element->exclusiveMethodOfB() << std::endl;
}
};
// 具体访问者2:导出
class ConcreteVisitor2 : public Visitor {
public:
void visitConcreteElementA(const ConcreteElementA* element) override {
std::cout << "ConcreteVisitor2: Exporting " << element->exclusiveMethodOfA() << std::endl;
}
void visitConcreteElementB(const ConcreteElementB* element) override {
std::cout << "ConcreteVisitor2: Exporting " << element->exclusiveMethodOfB() << std::endl;
}
};
// 对象结构
class ObjectStructure {
private:
std::vector<std::unique_ptr<Element>> elements_;
public:
void add(std::unique_ptr<Element> element) {
elements_.push_back(std::move(element));
}
void accept(Visitor* visitor) {
for (const auto& element : elements_) {
element->accept(visitor);
}
}
};
int main() {
ObjectStructure structure;
structure.add(std::make_unique<ConcreteElementA>());
structure.add(std::make_unique<ConcreteElementB>());
std::cout << "The client code works with all visitors via the base Visitor interface:" << std::endl;
auto visitor1 = std::make_unique<ConcreteVisitor1>();
structure.accept(visitor1.get());
std::cout << "\nIt allows the same client code to work with different types of visitors:" << std::endl;
auto visitor2 = std::make_unique<ConcreteVisitor2>();
structure.accept(visitor2.get());
return 0;
}
1.4.8.4 优缺点
优点:
-
可以在不修改元素类的情况下,添加新的操作。
-
将相关的操作集中到一个访问者中,方便管理。
-
可以访问不同的元素类,处理不同的情况。
缺点:
-
违反了开闭原则,当添加新的元素时,所有的访问者都需要修改。
-
破坏了封装性,访问者需要访问元素的内部细节。
-
元素需要暴露自己的内部状态给访问者。
1.4.9 4.9 责任链模式(Chain of Responsibility)
1.4.9.1 意图
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
1.4.9.2 适用场景
-
有多个对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
-
你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
-
可动态定义一个处理请求的对象链。
-
常见应用:MFC 消息处理、中间件、权限校验链、异常处理等。
1.4.9.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <memory>
// 处理者接口
class Handler {
public:
virtual ~Handler() = default;
virtual void setNext(std::unique_ptr<Handler> next) = 0;
virtual void handle(const std::string& request) = 0;
};
// 基础处理者
class BaseHandler : public Handler {
private:
std::unique_ptr<Handler> next_;
public:
BaseHandler() : next_(nullptr) {}
void setNext(std::unique_ptr<Handler> next) override {
next_ = std::move(next);
}
void handle(const std::string& request) override {
if (next_) {
next_->handle(request);
}
}
};
// 具体处理者1:认证
class AuthenticationHandler : public BaseHandler {
public:
void handle(const std::string& request) override {
if (request == "auth") {
std::cout << "AuthenticationHandler: Authenticated the request." << std::endl;
} else {
std::cout << "AuthenticationHandler: Passing..." << std::endl;
BaseHandler::handle(request);
}
}
};
// 具体处理者2:授权
class AuthorizationHandler : public BaseHandler {
public:
void handle(const std::string& request) override {
if (request == "authorize") {
std::cout << "AuthorizationHandler: Authorized the request." << std::endl;
} else {
std::cout << "AuthorizationHandler: Passing..." << std::endl;
BaseHandler::handle(request);
}
}
};
// 具体处理者3:日志
class LoggingHandler : public BaseHandler {
public:
void handle(const std::string& request) override {
std::cout << "LoggingHandler: Logging the request: " << request << std::endl;
BaseHandler::handle(request);
}
};
int main() {
// 构建责任链
auto auth = std::make_unique<AuthenticationHandler>();
auto authorize = std::make_unique<AuthorizationHandler>();
auto logging = std::make_unique<LoggingHandler>();
auth->setNext(std::move(authorize));
auth->getNext()->setNext(std::move(logging));
// 发送请求
std::cout << "Chain: Auth -> Authorize -> Logging" << std::endl;
std::cout << "\nSending request 'auth':" << std::endl;
auth->handle("auth");
std::cout << "\nSending request 'authorize':" << std::endl;
auth->handle("authorize");
std::cout << "\nSending request 'other':" << std::endl;
auth->handle("other");
return 0;
}
1.4.9.4 优缺点
优点:
-
可以动态地改变链的结构,添加或删除处理者。
-
发送者和接收者解耦,发送者不需要知道谁处理了请求。
-
符合开闭原则,添加新的处理者很容易。
-
可以控制请求的处理顺序。
缺点:
-
请求可能会一直到链的末尾都没有被处理,需要处理这种情况。
-
调试可能会比较困难,因为请求的处理路径是动态的。
1.4.10 4.10 迭代器模式(Iterator)
1.4.10.1 意图
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
1.4.10.2 适用场景
-
访问一个聚合对象的内容而无需暴露它的内部表示。
-
支持对聚合对象的多种遍历。
-
为遍历不同的聚合结构提供一个统一的接口。
-
常见应用:STL 迭代器、容器遍历等。
1.4.10.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// 前向声明
template <typename T>
class Iterator;
// 聚合接口
template <typename T>
class Aggregate {
public:
virtual ~Aggregate() = default;
virtual std::unique_ptr<Iterator<T>> createIterator() = 0;
};
// 迭代器接口
template <typename T>
class Iterator {
public:
virtual ~Iterator() = default;
virtual T first() = 0;
virtual T next() = 0;
virtual bool isDone() const = 0;
virtual T currentItem() const = 0;
};
// 具体聚合
class ConcreteAggregate : public Aggregate<int> {
private:
std::vector<int> data_;
public:
ConcreteAggregate() {
data_ = {1, 2, 3, 4, 5};
}
std::unique_ptr<Iterator<int>> createIterator() override;
int getItem(int index) const {
return data_[index];
}
int getSize() const {
return data_.size();
}
};
// 具体迭代器
class ConcreteIterator : public Iterator<int> {
private:
const ConcreteAggregate* aggregate_;
int index_;
public:
ConcreteIterator(const ConcreteAggregate* aggregate)
: aggregate_(aggregate), index_(0) {}
int first() override {
index_ = 0;
return currentItem();
}
int next() override {
index_++;
if (!isDone()) {
return currentItem();
}
return -1;
}
bool isDone() const override {
return index_ >= aggregate_->getSize();
}
int currentItem() const override {
return aggregate_->getItem(index_);
}
};
std::unique_ptr<Iterator<int>> ConcreteAggregate::createIterator() {
return std::make_unique<ConcreteIterator>(this);
}
int main() {
ConcreteAggregate aggregate;
auto iterator = aggregate.createIterator();
std::cout << "Iterating over the aggregate:" << std::endl;
for (iterator->first(); !iterator->isDone(); iterator->next()) {
std::cout << iterator->currentItem() << " ";
}
std::cout << std::endl;
return 0;
}
1.4.10.4 优缺点
优点:
-
支持以不同的方式遍历一个聚合对象。
-
简化了聚合对象的接口,不需要暴露内部结构。
-
可以同时对一个聚合进行多个遍历。
-
符合开闭原则,添加新的聚合或迭代器都很容易。
缺点:
-
对于简单的聚合,使用迭代器可能会有点多余。
-
增加了类的数量。
1.4.11 4.11 解释器模式(Interpreter)
1.4.11.1 意图
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
1.4.11.2 适用场景
-
当有一个语言需要解释执行,并且你可将该语言中的句子表示为抽象语法树时。
-
当文法相对简单,并且效率不是主要问题的时候。
-
常见应用:简单的表达式解释器、规则引擎、DSL 等。
1.4.11.3 C++ 实现
cpp
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <stack>
// 上下文:存储变量
class Context {
private:
std::unordered_map<std::string, int> variables_;
public:
void setVariable(const std::string& name, int value) {
variables_[name] = value;
}
int getVariable(const std::string& name) const {
return variables_.at(name);
}
};
// 表达式接口
class Expression {
public:
virtual ~Expression() = default;
virtual int interpret(const Context& context) const = 0;
};
// 变量表达式
class VariableExpression : public Expression {
private:
std::string name_;
public:
VariableExpression(const std::string& name) : name_(name) {}
int interpret(const Context& context) const override {
return context.getVariable(name_);
}
};
// 数字表达式
class NumberExpression : public Expression {
private:
int number_;
public:
NumberExpression(int number) : number_(number) {}
int interpret(const Context& context) const override {
return number_;
}
};
// 加法表达式
class AddExpression : public Expression {
private:
std::unique_ptr<Expression> left_;
std::unique_ptr<Expression> right_;
public:
AddExpression(std::unique_ptr<Expression> left, std::unique_ptr<Expression> right)
: left_(std::move(left)), right_(std::move(right)) {}
int interpret(const Context& context) const override {
return left_->interpret(context) + right_->interpret(context);
}
};
// 减法表达式
class SubtractExpression : public Expression {
private:
std::unique_ptr<Expression> left_;
std::unique_ptr<Expression> right_;
public:
SubtractExpression(std::unique_ptr<Expression> left, std::unique_ptr<Expression> right)
: left_(std::move(left)), right_(std::move(right)) {}
int interpret(const Context& context) const override {
return left_->interpret(context) - right_->interpret(context);
}
};
int main() {
Context context;
context.setVariable("x", 10);
context.setVariable("y", 5);
// 构建表达式:x + y - 3
auto x = std::make_unique<VariableExpression>("x");
auto y = std::make_unique<VariableExpression>("y");
auto three = std::make_unique<NumberExpression>(3);
auto add = std::make_unique<AddExpression>(std::move(x), std::move(y));
auto expression = std::make_unique<SubtractExpression>(std::move(add), std::move(three));
std::cout << "x + y - 3 = " << expression->interpret(context) << std::endl;
return 0;
}
1.4.11.4 优缺点
优点:
-
可以很容易地改变和扩展文法。
-
每个文法规则都可以表示为一个类,容易实现。
-
可以实现简单的 DSL。
缺点:
-
对于复杂的文法,会产生很多类,难以维护。
-
效率比较低,因为递归调用很多。
-
只适合简单的文法。
1.5 五、设计模式选择指南
学完了所有的设计模式,你可能会问:我该什么时候用哪个模式?这里给你一个简单的选择指南:
1.5.1 按需求选择
| 需求 | 推荐模式 |
|---|---|
| 需要全局唯一的实例 | 单例模式 |
| 需要灵活创建对象,解耦创建和使用 | 工厂方法 / 抽象工厂 |
| 需要分步构建复杂对象 | 建造者模式 |
| 需要快速复制大量相似对象 | 原型模式 |
| 需要兼容不兼容的接口 | 适配器模式 |
| 需要分离两个独立变化的维度 | 桥接模式 |
| 需要处理树形结构,统一处理整体和部分 | 组合模式 |
| 需要动态给对象添加功能 | 装饰器模式 |
| 需要简化复杂子系统的接口 | 外观模式 |
| 需要共享大量细粒度对象,节省内存 | 享元模式 |
| 需要控制对对象的访问 | 代理模式 |
| 需要链式处理请求 | 责任链模式 |
| 需要将请求封装为对象,支持撤销 / 队列 | 命令模式 |
| 需要一对多的通知机制 | 观察者模式 |
| 需要保存和恢复对象状态 | 备忘录模式 |
| 需要减少对象间的复杂交互 | 中介者模式 |
| 需要封装可切换的算法 | 策略模式 |
| 需要固定流程,可变步骤 | 模板方法模式 |
| 需要根据状态改变行为 | 状态模式 |
| 需要统一遍历不同的容器 | 迭代器模式 |
| 需要为数据结构添加新操作 | 访问者模式 |
| 需要解释简单的语法规则 | 解释器模式 |
1.5.2 针对不同系统的选择
1.5.2.1 客户端应用
-
UI 界面:观察者模式(状态通知)、状态模式(UI 状态)、命令模式(撤销 / 重做)
-
业务逻辑:策略模式(算法切换)、模板方法(固定流程)
-
网络通信:代理模式(请求代理)、适配器模式(API 适配)
1.5.2.2 嵌入式 / MCU
-
传感器交互:观察者模式(数据更新)、状态模式(传感器状态)
-
硬件控制:命令模式(控制命令)、模板方法(固定流程)
-
资源管理:单例模式(全局资源)、享元模式(共享资源)
1.5.2.3 SDK 开发
-
接口设计:工厂模式、抽象工厂、建造者模式
-
扩展性:策略模式、观察者模式、模板方法
-
兼容性:适配器模式、桥接模式、代理模式
-
易用性:外观模式、单例模式
1.6 总结
设计模式不是银弹,它只是解决特定问题的经验总结。不要为了使用模式而使用模式,要根据实际的问题来选择合适的模式。
学习设计模式的过程,也是学习面向对象设计思想的过程。当你理解了这些模式背后的设计原则,你就能够写出更加优雅、可维护、可扩展的代码。
希望这个指南能够帮助你彻底掌握 C/C++ 中的设计模式,成为一个更好的开发者。
(注:文档部分内容可能由 AI 生成)