本章讲解 23 种设计模式并提供 C++ 代码示例。
# 23 种设计模式完整指南 - C++ 实现详解
# 目录
# 设计模式概览
设计模式是解决软件设计中常见问题的可重用方案。它们提供了经过验证的解决方案,帮助开发者编写更优雅、可维护和可扩展的代码。
# 设计模式分类
| 类型 | 数量 | 描述 | 难度 |
|---|---|---|---|
| 创建型模式 | 5 种 | 关注对象的创建过程 | ⭐⭐ |
| 结构型模式 | 7 种 | 处理类或对象的组合 | ⭐⭐⭐ |
| 行为型模式 | 11 种 | 描述对象间的交互 | ⭐⭐⭐⭐ |
# 设计原则
# SOLID 原则
- 单一职责原则 (SRP) - 一个类应该只有一个职责
- 开放封闭原则 (OCP) - 对扩展开放,对修改关闭
- 里氏替换原则 (LSP) - 子类应该能够替换父类
- 接口隔离原则 (ISP) - 客户端不应该被迫依赖它们不使用的接口
- 依赖倒置原则 (DIP) - 依赖抽象,而不是具体实现
# 其他重要原则
- 组合复用原则 - 优先使用组合而不是继承
- 迪米特法则 - 最少知识原则
- 面向接口编程 - 而不是面向实现编程
- 封装变化 - 将变化的部分封装起来
# 创建型模式
创建型模式关注对象的创建过程,它们将系统与对象的创建、组合和表示方式解耦。
# 1. 单例模式 (Singleton)
概念:确保一个类只有一个实例,并提供一个全局访问点。
使用场景:
- 配置管理器
- 日志记录器
- 数据库连接池
- 缓存管理器
C++ 实现:
class Singleton { | |
private: | |
// 私有构造函数防止外部实例化 | |
Singleton() {} | |
// 删除拷贝构造函数和赋值运算符 | |
Singleton(const Singleton&) = delete; | |
Singleton& operator=(const Singleton&) = delete; | |
public: | |
// 线程安全的静态方法获取实例 | |
static Singleton& getInstance() { | |
static Singleton instance; | |
return instance; | |
} | |
void doSomething() { | |
std::cout << "Singleton operation" << std::endl; | |
} | |
}; | |
// 使用示例 | |
int main() { | |
Singleton& instance = Singleton::getInstance(); | |
instance.doSomething(); | |
return 0; | |
} |
优点:
- 严格控制实例数量
- 提供全局访问点
- 延迟初始化(懒汉式)
缺点:
- 可能隐藏依赖关系
- 单元测试困难
- 多线程需要考虑线程安全
# 2. 工厂方法模式 (Factory Method)
概念:定义一个创建对象的接口,让子类决定实例化哪个类。
使用场景:
- 对象创建需要灵活性
- 不知道具体需要创建的对象类型
- 希望将对象创建延迟到子类
C++ 实现:
// 产品接口 | |
class Product { | |
public: | |
virtual ~Product() {} | |
virtual void use() = 0; | |
}; | |
// 具体产品 A | |
class ConcreteProductA : public Product { | |
public: | |
void use() override { | |
std::cout << "Using Product A" << std::endl; | |
} | |
}; | |
// 具体产品 B | |
class ConcreteProductB : public Product { | |
public: | |
void use() override { | |
std::cout << "Using Product B" << std::endl; | |
} | |
}; | |
// 创建者抽象类 | |
class Creator { | |
public: | |
virtual ~Creator() {} | |
// 工厂方法 | |
virtual std::unique_ptr<Product> createProduct() = 0; | |
// 模板方法,使用工厂方法创建的产品 | |
void someOperation() { | |
auto product = createProduct(); | |
product->use(); | |
} | |
}; | |
// 具体创建者 A | |
class ConcreteCreatorA : public Creator { | |
public: | |
std::unique_ptr<Product> createProduct() override { | |
return std::make_unique<ConcreteProductA>(); | |
} | |
}; | |
// 具体创建者 B | |
class ConcreteCreatorB : public Creator { | |
public: | |
std::unique_ptr<Product> createProduct() override { | |
return std::make_unique<ConcreteProductB>(); | |
} | |
}; |
优点:
- 解耦对象的创建和使用
- 符合开闭原则
- 更容易扩展新产品类型
缺点:
- 增加了类的数量
- 增加了系统复杂度
# 3. 抽象工厂模式 (Abstract Factory)
概念:提供一个接口,用于创建相关或依赖对象的家族,而无需指定具体类。
使用场景:
- 需要创建一系列相关对象
- 系统需要独立于产品的创建、组合和表示
- 需要提供产品类库,只暴露接口
C++ 实现:
// 抽象产品 A | |
class AbstractProductA { | |
public: | |
virtual ~AbstractProductA() {} | |
virtual void operationA() = 0; | |
}; | |
// 抽象产品 B | |
class AbstractProductB { | |
public: | |
virtual ~AbstractProductB() {} | |
virtual void operationB() = 0; | |
}; | |
// 具体产品 A1 | |
class ConcreteProductA1 : public AbstractProductA { | |
public: | |
void operationA() override { | |
std::cout << "Product A1 operation" << std::endl; | |
} | |
}; | |
// 具体产品 A2 | |
class ConcreteProductA2 : public AbstractProductA { | |
public: | |
void operationA() override { | |
std::cout << "Product A2 operation" << std::endl; | |
} | |
}; | |
// 具体产品 B1 | |
class ConcreteProductB1 : public AbstractProductB { | |
public: | |
void operationB() override { | |
std::cout << "Product B1 operation" << std::endl; | |
} | |
}; | |
// 具体产品 B2 | |
class ConcreteProductB2 : public AbstractProductB { | |
public: | |
void operationB() override { | |
std::cout << "Product B2 operation" << std::endl; | |
} | |
}; | |
// 抽象工厂 | |
class AbstractFactory { | |
public: | |
virtual ~AbstractFactory() {} | |
virtual std::unique_ptr<AbstractProductA> createProductA() = 0; | |
virtual std::unique_ptr<AbstractProductB> createProductB() = 0; | |
}; | |
// 具体工厂 1 | |
class ConcreteFactory1 : public AbstractFactory { | |
public: | |
std::unique_ptr<AbstractProductA> createProductA() override { | |
return std::make_unique<ConcreteProductA1>(); | |
} | |
std::unique_ptr<AbstractProductB> createProductB() override { | |
return std::make_unique<ConcreteProductB1>(); | |
} | |
}; | |
// 具体工厂 2 | |
class ConcreteFactory2 : public AbstractFactory { | |
public: | |
std::unique_ptr<AbstractProductA> createProductA() override { | |
return std::make_unique<ConcreteProductA2>(); | |
} | |
std::unique_ptr<AbstractProductB> createProductB() override { | |
return std::make_unique<ConcreteProductB2>(); | |
} | |
}; |
优点:
- 确保产品族的一致性
- 易于交换产品系列
- 符合开闭原则
缺点:
- 难以支持新种类的产品
- 增加了系统复杂度
# 4. 建造者模式 (Builder)
概念:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用场景:
- 创建复杂对象
- 对象的构建过程独立于其组成部分
- 需要构建不同表示的复杂对象
C++ 实现:
// 产品类 | |
class Product { | |
private: | |
std::string partA_; | |
std::string partB_; | |
std::string partC_; | |
public: | |
void setPartA(const std::string& part) { partA_ = part; } | |
void setPartB(const std::string& part) { partB_ = part; } | |
void setPartC(const std::string& part) { partC_ = part; } | |
void show() { | |
std::cout << "Product parts: " << partA_ << ", " << partB_ << ", " << partC_ << std::endl; | |
} | |
}; | |
// 抽象建造者 | |
class Builder { | |
public: | |
virtual ~Builder() {} | |
virtual void buildPartA() = 0; | |
virtual void buildPartB() = 0; | |
virtual void buildPartC() = 0; | |
virtual std::unique_ptr<Product> getResult() = 0; | |
}; | |
// 具体建造者 | |
class ConcreteBuilder : public Builder { | |
private: | |
std::unique_ptr<Product> product_; | |
public: | |
ConcreteBuilder() : product_(std::make_unique<Product>()) {} | |
void buildPartA() override { | |
product_->setPartA("PartA"); | |
} | |
void buildPartB() override { | |
product_->setPartB("PartB"); | |
} | |
void buildPartC() override { | |
product_->setPartC("PartC"); | |
} | |
std::unique_ptr<Product> getResult() override { | |
return std::move(product_); | |
} | |
}; | |
// 导演者 | |
class Director { | |
private: | |
std::unique_ptr<Builder> builder_; | |
public: | |
void setBuilder(std::unique_ptr<Builder> builder) { | |
builder_ = std::move(builder); | |
} | |
std::unique_ptr<Product> construct() { | |
builder_->buildPartA(); | |
builder_->buildPartB(); | |
builder_->buildPartC(); | |
return builder_->getResult(); | |
} | |
}; |
优点:
- 构建过程清晰
- 易于扩展新的建造者
- 更好地控制构建过程
缺点:
- 增加了类的数量
- 只适用于创建复杂对象
# 5. 原型模式 (Prototype)
概念:通过复制现有的实例来创建新的实例,而不是通过新建类。
使用场景:
- 创建对象成本较高
- 需要大量相似对象
- 运行时动态指定对象类型
C++ 实现:
#include <memory> | |
#include <string> | |
#include <unordered_map> | |
// 原型接口 | |
class Prototype { | |
public: | |
virtual ~Prototype() {} | |
virtual std::unique_ptr<Prototype> clone() = 0; | |
virtual void display() = 0; | |
}; | |
// 具体原型 A | |
class ConcretePrototypeA : public Prototype { | |
private: | |
std::string data_; | |
public: | |
ConcretePrototypeA(const std::string& data) : data_(data) {} | |
std::unique_ptr<Prototype> clone() override { | |
return std::make_unique<ConcretePrototypeA>(*this); | |
} | |
void display() override { | |
std::cout << "Prototype A: " << data_ << std::endl; | |
} | |
}; | |
// 具体原型 B | |
class ConcretePrototypeB : public Prototype { | |
private: | |
int value_; | |
public: | |
ConcretePrototypeB(int value) : value_(value) {} | |
std::unique_ptr<Prototype> clone() override { | |
return std::make_unique<ConcretePrototypeB>(*this); | |
} | |
void display() override { | |
std::cout << "Prototype B: " << value_ << std::endl; | |
} | |
}; | |
// 原型管理器 | |
class PrototypeManager { | |
private: | |
std::unordered_map<std::string, std::unique_ptr<Prototype>> prototypes_; | |
public: | |
void registerPrototype(const std::string& name, std::unique_ptr<Prototype> prototype) { | |
prototypes_[name] = std::move(prototype); | |
} | |
std::unique_ptr<Prototype> create(const std::string& name) { | |
if (prototypes_.find(name) != prototypes_.end()) { | |
return prototypes_[name]->clone(); | |
} | |
return nullptr; | |
} | |
}; |
优点:
- 避免了重复的初始化过程
- 可以动态添加或删除原型
- 隐藏了具体的产品类
缺点:
- 需要实现克隆方法
- 深拷贝和浅拷贝的问题
# 结构型模式
结构型模式处理类或对象的组合,它们描述如何将类或对象组合成更大的结构。
# 6. 适配器模式 (Adapter)
概念:将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而无法一起工作的类可以一起工作。
使用场景:
- 需要使用现有类,但其接口不符合要求
- 希望创建一个可重用的类,与多个不相关的类合作
- 需要统一多个类的接口
C++ 实现:
// 目标接口 | |
class Target { | |
public: | |
virtual ~Target() {} | |
virtual void request() = 0; | |
}; | |
// 需要适配的类 | |
class Adaptee { | |
public: | |
void specificRequest() { | |
std::cout << "Adaptee: specific request" << std::endl; | |
} | |
}; | |
// 对象适配器 | |
class ObjectAdapter : public Target { | |
private: | |
std::unique_ptr<Adaptee> adaptee_; | |
public: | |
ObjectAdapter() : adaptee_(std::make_unique<Adaptee>()) {} | |
void request() override { | |
std::cout << "Adapter: translating request..." << std::endl; | |
adaptee_->specificRequest(); | |
} | |
}; | |
// 类适配器 | |
class ClassAdapter : public Target, private Adaptee { | |
public: | |
void request() override { | |
std::cout << "ClassAdapter: translating request..." << std::endl; | |
specificRequest(); | |
} | |
}; |
优点:
- 提高了类的复用性
- 增加了类的透明度
- 灵活性好
缺点:
- 过多使用适配器会使系统凌乱
- 增加了系统的复杂度
# 7. 桥接模式 (Bridge)
概念:将抽象部分与它的实现部分分离,使它们可以独立变化。
使用场景:
- 需要在抽象和实现之间增加灵活性
- 抽象和实现都应该可以通过子类化扩展
- 实现的变化不应该影响客户端
C++ 实现:
// 实现接口 | |
class Implementor { | |
public: | |
virtual ~Implementor() {} | |
virtual void operationImpl() = 0; | |
}; | |
class ConcreteImplementorA : public Implementor { | |
public: | |
void operationImpl() override { | |
std::cout << "ConcreteImplementorA operation" << std::endl; | |
} | |
}; | |
class ConcreteImplementorB : public Implementor { | |
public: | |
void operationImpl() override { | |
std::cout << "ConcreteImplementorB operation" << std::endl; | |
} | |
}; | |
// 抽象接口 | |
class Abstraction { | |
protected: | |
std::unique_ptr<Implementor> implementor_; | |
public: | |
Abstraction(std::unique_ptr<Implementor> impl) : implementor_(std::move(impl)) {} | |
virtual ~Abstraction() {} | |
virtual void operation() { | |
implementor_->operationImpl(); | |
} | |
}; | |
class RefinedAbstraction : public Abstraction { | |
public: | |
using Abstraction::Abstraction; | |
void operation() override { | |
std::cout << "RefinedAbstraction: "; | |
implementor_->operationImpl(); | |
} | |
}; |
优点:
- 分离了接口和实现
- 提高了可扩展性
- 符合开闭原则
缺点:
- 增加了系统的理解和设计难度
- 需要正确识别出系统中两个独立变化的维度
# 8. 组合模式 (Composite)
概念:将对象组合成树形结构以表示 "部分 - 整体" 的层次结构,使得客户可以一致地对待单个对象和对象组合。
使用场景:
- 需要表示对象的部分 - 整体层次结构
- 希望用户忽略组合对象与单个对象的不同
- 需要统一地处理所有对象
C++ 实现:
#include <vector> | |
#include <algorithm> | |
// 组件接口 | |
class Component { | |
public: | |
virtual ~Component() {} | |
virtual void operation() = 0; | |
virtual void add(std::unique_ptr<Component> component) {} | |
virtual void remove(Component* component) {} | |
virtual Component* getChild(int index) { return nullptr; } | |
}; | |
// 叶子节点 | |
class Leaf : public Component { | |
private: | |
std::string name_; | |
public: | |
Leaf(const std::string& name) : name_(name) {} | |
void operation() override { | |
std::cout << "Leaf: " << name_ << std::endl; | |
} | |
}; | |
// 复合节点 | |
class Composite : public Component { | |
private: | |
std::vector<std::unique_ptr<Component>> children_; | |
std::string name_; | |
public: | |
Composite(const std::string& name) : name_(name) {} | |
void operation() override { | |
std::cout << "Composite: " << name_ << std::endl; | |
for (const auto& child : children_) { | |
child->operation(); | |
} | |
} | |
void add(std::unique_ptr<Component> component) override { | |
children_.push_back(std::move(component)); | |
} | |
void remove(Component* component) override { | |
children_.erase( | |
std::remove_if(children_.begin(), children_.end(), | |
[component](const std::unique_ptr<Component>& ptr) { | |
return ptr.get() == component; | |
}), | |
children_.end() | |
); | |
} | |
Component* getChild(int index) override { | |
if (index >= 0 && index < children_.size()) { | |
return children_[index].get(); | |
} | |
return nullptr; | |
} | |
}; |
优点:
- 简化了客户端代码
- 更容易添加新类型的组件
- 符合开闭原则
缺点:
- 难以限制组合中的组件类型
- 可能会使设计过于一般化
# 9. 装饰模式 (Decorator)
概念:动态地给一个对象添加一些额外的职责,提供了比继承更灵活的扩展功能的方式。
使用场景:
- 需要动态地给对象添加功能
- 需要撤销功能
- 当不能采用继承的方式扩展系统时
C++ 实现:
// 基础组件接口 | |
class Coffee { | |
public: | |
virtual ~Coffee() {} | |
virtual double cost() = 0; | |
virtual std::string description() = 0; | |
}; | |
// 具体组件 | |
class SimpleCoffee : public Coffee { | |
public: | |
double cost() override { | |
return 1.0; | |
} | |
std::string description() override { | |
return "Simple coffee"; | |
} | |
}; | |
// 装饰器基类 | |
class CoffeeDecorator : public Coffee { | |
protected: | |
std::unique_ptr<Coffee> coffee_; | |
public: | |
CoffeeDecorator(std::unique_ptr<Coffee> coffee) : coffee_(std::move(coffee)) {} | |
}; | |
// 具体装饰器 - 牛奶 | |
class MilkDecorator : public CoffeeDecorator { | |
public: | |
MilkDecorator(std::unique_ptr<Coffee> coffee) : CoffeeDecorator(std::move(coffee)) {} | |
double cost() override { | |
return coffee_->cost() + 0.5; | |
} | |
std::string description() override { | |
return coffee_->description() + ", milk"; | |
} | |
}; | |
// 具体装饰器 - 糖 | |
class SugarDecorator : public CoffeeDecorator { | |
public: | |
SugarDecorator(std::unique_ptr<Coffee> coffee) : CoffeeDecorator(std::move(coffee)) {} | |
double cost() override { | |
return coffee_->cost() + 0.2; | |
} | |
std::string description() override { | |
return coffee_->description() + ", sugar"; | |
} | |
}; |
优点:
- 比继承更灵活
- 避免了类爆炸
- 符合开闭原则
缺点:
- 会产生许多小对象
- 调试困难
# 10. 外观模式 (Facade)
概念:为子系统中的一组接口提供一个一致的界面,使得子系统更容易使用。
使用场景:
- 需要简化复杂子系统的使用
- 需要将子系统与客户端解耦
- 需要分层设计
C++ 实现:
// 子系统类 | |
class CPU { | |
public: | |
void freeze() { std::cout << "CPU: freeze" << std::endl; } | |
void jump(long position) { std::cout << "CPU: jump to " << position << std::endl; } | |
void execute() { std::cout << "CPU: execute" << std::endl; } | |
}; | |
class Memory { | |
public: | |
void load(long position, const std::string& data) { | |
std::cout << "Memory: load data at " << position << std::endl; | |
} | |
}; | |
class HardDrive { | |
public: | |
std::string read(long lba, int size) { | |
std::cout << "HardDrive: read " << size << " bytes from " << lba << std::endl; | |
return "data"; | |
} | |
}; | |
// 外观类 | |
class ComputerFacade { | |
private: | |
CPU cpu_; | |
Memory memory_; | |
HardDrive hardDrive_; | |
public: | |
void start() { | |
std::cout << "Computer starting..." << std::endl; | |
cpu_.freeze(); | |
memory_.load(0, hardDrive_.read(0, 1024)); | |
cpu_.jump(0); | |
cpu_.execute(); | |
std::cout << "Computer started!" << std::endl; | |
} | |
}; |
优点:
- 简化了接口
- 解耦了客户端和子系统
- 更好的层次划分
缺点:
- 可能过于简化,无法满足特殊需求
- 不符合开闭原则(可能需要修改外观类)
# 11. 享元模式 (Flyweight)
概念:运用共享技术来有效地支持大量细粒度对象的复用。
使用场景:
- 系统中存在大量相似对象
- 需要缓冲池的场景
- 对象的大部分状态可以外部化
C++ 实现:
#include <map> | |
// 享元接口 | |
class Flyweight { | |
public: | |
virtual ~Flyweight() {} | |
virtual void operation(const std::string& extrinsicState) = 0; | |
}; | |
// 具体享元 | |
class ConcreteFlyweight : public Flyweight { | |
private: | |
std::string intrinsicState_; | |
public: | |
ConcreteFlyweight(const std::string& state) : intrinsicState_(state) {} | |
void operation(const std::string& extrinsicState) override { | |
std::cout << "Intrinsic: " << intrinsicState_ | |
<< ", Extrinsic: " << extrinsicState << std::endl; | |
} | |
}; | |
// 享元工厂 | |
class FlyweightFactory { | |
private: | |
std::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<ConcreteFlyweight>(key); | |
} | |
return flyweights_[key].get(); | |
} | |
size_t getFlyweightCount() const { | |
return flyweights_.size(); | |
} | |
}; |
优点:
- 减少了内存使用
- 提高了性能
- 集中管理共享对象
缺点:
- 增加了系统复杂度
- 需要区分内部状态和外部状态
# 12. 代理模式 (Proxy)
概念:为其他对象提供一种代理以控制对这个对象的访问。
使用场景:
- 需要控制对对象的访问
- 需要延迟加载
- 需要远程访问
C++ 实现:
// 主题接口 | |
class Subject { | |
public: | |
virtual ~Subject() {} | |
virtual void request() = 0; | |
}; | |
// 真实主题 | |
class RealSubject : public Subject { | |
public: | |
void request() override { | |
std::cout << "RealSubject: handling request" << std::endl; | |
} | |
}; | |
// 代理类 | |
class Proxy : public Subject { | |
private: | |
std::unique_ptr<RealSubject> realSubject_; | |
void checkAccess() { | |
std::cout << "Proxy: checking access" << std::endl; | |
} | |
void logAccess() { | |
std::cout << "Proxy: logging access" << std::endl; | |
} | |
public: | |
void request() override { | |
checkAccess(); | |
if (!realSubject_) { | |
realSubject_ = std::make_unique<RealSubject>(); | |
} | |
realSubject_->request(); | |
logAccess(); | |
} | |
}; |
优点:
- 职责清晰
- 高扩展性
- 智能化
缺点:
- 增加了系统复杂度
- 请求处理速度变慢
# 行为型模式
行为型模式关注对象之间的通信和职责分配,它们描述算法和对象间职责的分配。
# 13. 职责链模式 (Chain of Responsibility)
概念:将请求沿着处理链传递,直到有一个对象处理它。
使用场景:
- 多个对象可以处理同一请求
- 不明确指定接收者
- 需要动态指定处理链
C++ 实现:
class Handler { | |
protected: | |
std::unique_ptr<Handler> next_; | |
public: | |
virtual ~Handler() {} | |
void setNext(std::unique_ptr<Handler> next) { | |
next_ = std::move(next); | |
} | |
virtual void handleRequest(const std::string& request) { | |
if (next_) { | |
next_->handleRequest(request); | |
} | |
} | |
}; | |
class ConcreteHandlerA : public Handler { | |
public: | |
void handleRequest(const std::string& request) override { | |
if (request == "A") { | |
std::cout << "Handler A handled request: " << request << std::endl; | |
} else if (next_) { | |
next_->handleRequest(request); | |
} | |
} | |
}; | |
class ConcreteHandlerB : public Handler { | |
public: | |
void handleRequest(const std::string& request) override { | |
if (request == "B") { | |
std::cout << "Handler B handled request: " << request << std::endl; | |
} else if (next_) { | |
next_->handleRequest(request); | |
} | |
} | |
}; |
优点:
- 降低了耦合度
- 增强了系统的可扩展性
- 增强了给对象指派职责的灵活性
缺点:
- 不能保证请求一定被接收
- 系统性能可能受到影响
- 调试不方便
# 14. 命令模式 (Command)
概念:将请求封装成对象,以便使用不同的请求、队列或日志请求来参数化其他对象。
使用场景:
- 需要将请求调用者和接收者解耦
- 需要支持撤销和重做
- 需要支持命令队列和日志
C++ 实现:
// 接收者 | |
class Receiver { | |
public: | |
void action() { | |
std::cout << "Receiver: performing action" << std::endl; | |
} | |
void undoAction() { | |
std::cout << "Receiver: undoing action" << std::endl; | |
} | |
}; | |
// 命令接口 | |
class Command { | |
public: | |
virtual ~Command() {} | |
virtual void execute() = 0; | |
virtual void undo() = 0; | |
}; | |
// 具体命令 | |
class ConcreteCommand : public Command { | |
private: | |
std::unique_ptr<Receiver> receiver_; | |
public: | |
ConcreteCommand(std::unique_ptr<Receiver> receiver) : receiver_(std::move(receiver)) {} | |
void execute() override { | |
receiver_->action(); | |
} | |
void undo() override { | |
receiver_->undoAction(); | |
} | |
}; | |
// 调用者 | |
class Invoker { | |
private: | |
std::stack<std::unique_ptr<Command>> commandHistory_; | |
public: | |
void executeCommand(std::unique_ptr<Command> command) { | |
command->execute(); | |
commandHistory_.push(std::move(command)); | |
} | |
void undoCommand() { | |
if (!commandHistory_.empty()) { | |
commandHistory_.top()->undo(); | |
commandHistory_.pop(); | |
} | |
} | |
}; |
优点:
- 降低了系统耦合度
- 新的命令可以很容易地添加到系统中
- 可以比较容易地设计一个命令队列和宏命令
缺点:
- 可能会导致某些系统有过多的具体命令类
# 15. 解释器模式 (Interpreter)
概念:为语言的语法表示定义一个解释器,使用该解释器来解释语言中的句子。
使用场景:
- 语言的语法比较简单
- 效率不是主要考虑因素
- 需要解释执行的语言
C++ 实现:
// 上下文 | |
class Context { | |
private: | |
std::string input_; | |
public: | |
Context(const std::string& input) : input_(input) {} | |
std::string getInput() const { return input_; } | |
void setInput(const std::string& input) { input_ = input; } | |
}; | |
// 抽象表达式 | |
class AbstractExpression { | |
public: | |
virtual ~AbstractExpression() {} | |
virtual bool interpret(Context& context) = 0; | |
}; | |
// 终结符表达式 | |
class TerminalExpression : public AbstractExpression { | |
private: | |
std::string data_; | |
public: | |
TerminalExpression(const std::string& data) : data_(data) {} | |
bool interpret(Context& context) override { | |
return context.getInput().find(data_) != std::string::npos; | |
} | |
}; | |
// 或表达式 | |
class OrExpression : public AbstractExpression { | |
private: | |
std::unique_ptr<AbstractExpression> expr1_; | |
std::unique_ptr<AbstractExpression> expr2_; | |
public: | |
OrExpression(std::unique_ptr<AbstractExpression> expr1, | |
std::unique_ptr<AbstractExpression> expr2) | |
: expr1_(std::move(expr1)), expr2_(std::move(expr2)) {} | |
bool interpret(Context& context) override { | |
return expr1_->interpret(context) || expr2_->interpret(context); | |
} | |
}; | |
// 与表达式 | |
class AndExpression : public AbstractExpression { | |
private: | |
std::unique_ptr<AbstractExpression> expr1_; | |
std::unique_ptr<AbstractExpression> expr2_; | |
public: | |
AndExpression(std::unique_ptr<AbstractExpression> expr1, | |
std::unique_ptr<AbstractExpression> expr2) | |
: expr1_(std::move(expr1)), expr2_(std::move(expr2)) {} | |
bool interpret(Context& context) override { | |
return expr1_->interpret(context) && expr2_->interpret(context); | |
} | |
}; |
优点:
- 易于改变和扩展文法
- 实现文法比较简单
缺点:
- 复杂的文法难以维护
- 解释器模式会引起类膨胀
- 解释器模式采用递归调用方法
# 16. 迭代器模式 (Iterator)
概念:提供一种方法顺序访问聚合对象中的各个元素,而不暴露其内部表示。
使用场景:
- 需要访问聚合对象的内容而不暴露内部表示
- 需要支持多种遍历方式
- 需要为不同的聚合结构提供统一的遍历接口
C++ 实现:
template<typename T> | |
class Iterator { | |
public: | |
virtual ~Iterator() {} | |
virtual bool hasNext() = 0; | |
virtual T next() = 0; | |
}; | |
// 聚合接口 | |
template<typename T> | |
class Aggregate { | |
public: | |
virtual ~Aggregate() {} | |
virtual std::unique_ptr<Iterator<T>> createIterator() = 0; | |
virtual void add(const T& item) = 0; | |
virtual T get(int index) = 0; | |
virtual int size() = 0; | |
}; | |
// 具体聚合 | |
template<typename T> | |
class ConcreteAggregate : public Aggregate<T> { | |
private: | |
std::vector<T> items_; | |
public: | |
std::unique_ptr<Iterator<T>> createIterator() override; | |
void add(const T& item) override { items_.push_back(item); } | |
T get(int index) override { return items_[index]; } | |
int size() override { return items_.size(); } | |
}; | |
// 具体迭代器 | |
template<typename T> | |
class ConcreteIterator : public Iterator<T> { | |
private: | |
ConcreteAggregate<T>* aggregate_; | |
int current_; | |
public: | |
ConcreteIterator(ConcreteAggregate<T>* aggregate) | |
: aggregate_(aggregate), current_(0) {} | |
bool hasNext() override { | |
return current_ < aggregate_->size(); | |
} | |
T next() override { | |
return aggregate_->get(current_++); | |
} | |
}; | |
template<typename T> | |
std::unique_ptr<Iterator<T>> ConcreteAggregate<T>::createIterator() { | |
return std::make_unique<ConcreteIterator<T>>(this); | |
} |
优点:
- 支持以不同的方式遍历一个聚合对象
- 迭代器简化了聚合类
- 在同一个聚合上可以有多个遍历
缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性
# 17. 中介者模式 (Mediator)
概念:用一个中介对象来封装一系列对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散。
使用场景:
- 一组对象以定义良好但是复杂的方式进行通信
- 想定制一个分布在多个类中的行为,而不想生成太多的子类
C++ 实现:
class Colleague; | |
class Mediator { | |
public: | |
virtual ~Mediator() {} | |
virtual void send(const std::string& message, Colleague* colleague) = 0; | |
}; | |
class Colleague { | |
protected: | |
Mediator* mediator_; | |
std::string name_; | |
public: | |
Colleague(Mediator* mediator, const std::string& name) | |
: mediator_(mediator), name_(name) {} | |
virtual void send(const std::string& message) { | |
mediator_->send(message, this); | |
} | |
virtual void receive(const std::string& message) { | |
std::cout << name_ << " received: " << message << std::endl; | |
} | |
std::string getName() const { return name_; } | |
}; | |
class ConcreteMediator : public Mediator { | |
private: | |
std::vector<Colleague*> colleagues_; | |
public: | |
void addColleague(Colleague* colleague) { | |
colleagues_.push_back(colleague); | |
} | |
void send(const std::string& message, Colleague* sender) override { | |
for (auto colleague : colleagues_) { | |
if (colleague != sender) { | |
colleague->receive(message); | |
} | |
} | |
} | |
}; |
优点:
- 简化了对象之间的交互
- 将各同事对象解耦
- 减少子类生成
缺点:
- 中介者会庞大,变得复杂难以维护
# 18. 备忘录模式 (Memento)
概念:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
使用场景:
- 需要保存和恢复数据的相关状态
- 提供一个可回滚的操作
- 需要监控对象的状态
C++ 实现:
// 备忘录 | |
class Memento { | |
private: | |
std::string state_; | |
public: | |
Memento(const std::string& state) : state_(state) {} | |
std::string getState() const { return state_; } | |
void setState(const std::string& state) { state_ = state; } | |
}; | |
// 原发器 | |
class Originator { | |
private: | |
std::string state_; | |
public: | |
void setState(const std::string& state) { | |
state_ = state; | |
std::cout << "Originator: state set to " << state_ << std::endl; | |
} | |
std::string getState() const { return state_; } | |
std::unique_ptr<Memento> saveStateToMemento() { | |
return std::make_unique<Memento>(state_); | |
} | |
void getStateFromMemento(Memento* memento) { | |
state_ = memento->getState(); | |
std::cout << "Originator: state restored to " << state_ << std::endl; | |
} | |
}; | |
// 管理者 | |
class CareTaker { | |
private: | |
std::vector<std::unique_ptr<Memento>> mementoList_; | |
public: | |
void add(std::unique_ptr<Memento> state) { | |
mementoList_.push_back(std::move(state)); | |
} | |
Memento* get(int index) { | |
return mementoList_[index].get(); | |
} | |
}; |
优点:
- 给用户提供了一种可以恢复状态的机制
- 实现了信息的封装
缺点:
- 消耗资源
- 如果状态数据很大,备忘录模式会非常消耗内存
# 19. 观察者模式 (Observer)
概念:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
使用场景:
- 一个对象的改变需要同时改变其他对象
- 一个对象必须通知其他对象,而不知道这些对象是谁
- 需要在系统中创建一个触发链
C++ 实现:
class Observer { | |
public: | |
virtual ~Observer() {} | |
virtual void update(const std::string& message) = 0; | |
}; | |
class Subject { | |
public: | |
virtual ~Subject() {} | |
virtual void attach(Observer* observer) = 0; | |
virtual void detach(Observer* observer) = 0; | |
virtual void notify(const std::string& message) = 0; | |
}; | |
class ConcreteSubject : public Subject { | |
private: | |
std::list<Observer*> observers_; | |
std::string state_; | |
public: | |
void attach(Observer* observer) override { | |
observers_.push_back(observer); | |
} | |
void detach(Observer* observer) override { | |
observers_.remove(observer); | |
} | |
void notify(const std::string& message) override { | |
for (auto observer : observers_) { | |
observer->update(message); | |
} | |
} | |
void setState(const std::string& state) { | |
state_ = state; | |
notify("State changed to: " + state_); | |
} | |
std::string getState() const { return state_; } | |
}; | |
class ConcreteObserver : public Observer { | |
private: | |
std::string name_; | |
ConcreteSubject* subject_; | |
public: | |
ConcreteObserver(const std::string& name, ConcreteSubject* subject) | |
: name_(name), subject_(subject) {} | |
void update(const std::string& message) override { | |
std::cout << name_ << " received update: " << message << std::endl; | |
} | |
}; |
优点:
- 观察者和被观察者是抽象耦合的
- 建立一套触发机制
缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
# 20. 状态模式 (State)
概念:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
使用场景:
- 对象的行为依赖于它的状态
- 需要在运行时根据状态改变行为
- 操作中有大量的条件分支
C++ 实现:
class Context; | |
class State { | |
public: | |
virtual ~State() {} | |
virtual void handle(Context& context) = 0; | |
virtual std::string getName() = 0; | |
}; | |
class Context { | |
private: | |
std::unique_ptr<State> state_; | |
public: | |
Context(std::unique_ptr<State> state) : state_(std::move(state)) {} | |
void setState(std::unique_ptr<State> state) { | |
std::cout << "Context: Transitioning to " << state->getName() << std::endl; | |
state_ = std::move(state); | |
} | |
void request() { | |
state_->handle(*this); | |
} | |
}; | |
class ConcreteStateA : public State { | |
public: | |
void handle(Context& context) override; | |
std::string getName() override { return "StateA"; } | |
}; | |
class ConcreteStateB : public State { | |
public: | |
void handle(Context& context) override; | |
std::string getName() override { return "StateB"; } | |
}; | |
void ConcreteStateA::handle(Context& context) { | |
std::cout << "StateA: handling request" << std::endl; | |
context.setState(std::make_unique<ConcreteStateB>()); | |
} | |
void ConcreteStateB::handle(Context& context) { | |
std::cout << "StateB: handling request" << std::endl; | |
context.setState(std::make_unique<ConcreteStateA>()); | |
} |
优点:
- 封装了转换规则
- 枚举可能的状态,在枚举状态之前需要确定状态种类
- 将所有与某个状态有关的行为放到一个类中
缺点:
- 状态模式的使用必然会增加系统类和对象的个数
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
- 状态模式对 "开闭原则" 的支持并不太好
# 21. 策略模式 (Strategy)
概念:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。
使用场景:
- 许多相关的类仅仅是行为有异
- 需要使用一个算法的不同变体
- 算法使用客户不应该知道的数据
C++ 实现:
class Strategy { | |
public: | |
virtual ~Strategy() {} | |
virtual void execute() = 0; | |
}; | |
class ConcreteStrategyA : public Strategy { | |
public: | |
void execute() override { | |
std::cout << "Strategy A: sorting array" << std::endl; | |
} | |
}; | |
class ConcreteStrategyB : public Strategy { | |
public: | |
void execute() override { | |
std::cout << "Strategy B: reverse sorting array" << std::endl; | |
} | |
}; | |
class Context { | |
private: | |
std::unique_ptr<Strategy> strategy_; | |
public: | |
void setStrategy(std::unique_ptr<Strategy> strategy) { | |
strategy_ = std::move(strategy); | |
} | |
void executeStrategy() { | |
if (strategy_) { | |
strategy_->execute(); | |
} | |
} | |
}; |
优点:
- 算法可以自由切换
- 避免使用多重条件判断
- 扩展性良好
缺点:
- 策略类会增多
- 所有策略类都需要对外暴露
# 22. 模板方法模式 (Template Method)
概念:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
使用场景:
- 有多个子类共有的方法,且逻辑相同
- 重要的、复杂的方法,可以考虑作为模板方法
C++ 实现:
class AbstractClass { | |
public: | |
void templateMethod() { | |
baseOperation1(); | |
requiredOperation1(); | |
baseOperation2(); | |
hook1(); | |
requiredOperation2(); | |
baseOperation3(); | |
hook2(); | |
} | |
protected: | |
void baseOperation1() { | |
std::cout << "AbstractClass: Base operation 1" << std::endl; | |
} | |
void baseOperation2() { | |
std::cout << "AbstractClass: Base operation 2" << std::endl; | |
} | |
void baseOperation3() { | |
std::cout << "AbstractClass: Base operation 3" << std::endl; | |
} | |
virtual void requiredOperation1() = 0; | |
virtual void requiredOperation2() = 0; | |
virtual void hook1() {} | |
virtual void hook2() {} | |
}; | |
class ConcreteClass1 : public AbstractClass { | |
protected: | |
void requiredOperation1() override { | |
std::cout << "ConcreteClass1: Required operation 1" << std::endl; | |
} | |
void requiredOperation2() override { | |
std::cout << "ConcreteClass1: Required operation 2" << std::endl; | |
} | |
void hook1() override { | |
std::cout << "ConcreteClass1: Hook 1" << std::endl; | |
} | |
}; | |
class ConcreteClass2 : public AbstractClass { | |
protected: | |
void requiredOperation1() override { | |
std::cout << "ConcreteClass2: Required operation 1" << std::endl; | |
} | |
void requiredOperation2() override { | |
std::cout << "ConcreteClass2: Required operation 2" << std::endl; | |
} | |
void hook2() override { | |
std::cout << "ConcreteClass2: Hook 2" << std::endl; | |
} | |
}; |
优点:
- 封装不变部分,扩展可变部分
- 提取公共代码,便于维护
- 行为由父类控制,子类实现
缺点:
- 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
# 23. 访问者模式 (Visitor)
概念:封装一些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
使用场景:
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作
C++ 实现:
class ConcreteElementA; | |
class ConcreteElementB; | |
class Visitor { | |
public: | |
virtual ~Visitor() {} | |
virtual void visit(ConcreteElementA& element) = 0; | |
virtual void visit(ConcreteElementB& element) = 0; | |
}; | |
class Element { | |
public: | |
virtual ~Element() {} | |
virtual void accept(Visitor& visitor) = 0; | |
}; | |
class ConcreteElementA : public Element { | |
public: | |
void accept(Visitor& visitor) override { | |
visitor.visit(*this); | |
} | |
std::string operationA() const { | |
return "ConcreteElementA"; | |
} | |
}; | |
class ConcreteElementB : public Element { | |
public: | |
void accept(Visitor& visitor) override { | |
visitor.visit(*this); | |
} | |
std::string operationB() const { | |
return "ConcreteElementB"; | |
} | |
}; | |
class ConcreteVisitor1 : public Visitor { | |
public: | |
void visit(ConcreteElementA& element) override { | |
std::cout << "Visitor1: " << element.operationA() << std::endl; | |
} | |
void visit(ConcreteElementB& element) override { | |
std::cout << "Visitor1: " << element.operationB() << std::endl; | |
} | |
}; | |
class ConcreteVisitor2 : public Visitor { | |
public: | |
void visit(ConcreteElementA& element) override { | |
std::cout << "Visitor2: " << element.operationA() << std::endl; | |
} | |
void visit(ConcreteElementB& element) override { | |
std::cout << "Visitor2: " << element.operationB() << std::endl; | |
} | |
}; |
优点:
- 符合单一职责原则
- 优秀的扩展性
- 灵活性
缺点:
- 具体元素对访问者公布细节,违反了迪米特原则
- 具体元素变更比较困难
- 违反了依赖倒置原则
# 实践应用
# 何时使用设计模式
✅ 适合使用的场景:
- 当遇到重复出现的设计问题时
- 需要提高代码的可维护性和可扩展性时
- 团队需要统一的代码结构和风格时
- 需要解耦系统组件时
- 需要支持未来的功能扩展时
❌ 不适合使用的场景:
- 过度使用设计模式
- 不理解问题本质就盲目应用
- 性能要求极高且模式带来额外开销
- 简单的功能不需要复杂的设计
# 模式选择指南
# 创建型模式选择
| 场景 | 推荐模式 |
|---|---|
| 只需要一个实例 | 单例模式 |
| 需要创建产品族 | 抽象工厂 |
| 需要逐步构建复杂对象 | 建造者模式 |
| 需要复制现有对象 | 原型模式 |
| 需要灵活创建单个产品 | 工厂方法 |
# 结构型模式选择
| 场景 | 推荐模式 |
|---|---|
| 接口不兼容 | 适配器模式 |
| 需要分离抽象和实现 | 桥接模式 |
| 需要树形结构 | 组合模式 |
| 需要动态添加功能 | 装饰模式 |
| 需要简化复杂接口 | 外观模式 |
| 需要优化大量相似对象 | 享元模式 |
| 需要控制对象访问 | 代理模式 |
# 行为型模式选择
| 场景 | 推荐模式 |
|---|---|
| 需要处理请求链 | 职责链模式 |
| 需要支持撤销重做 | 命令模式 |
| 需要解释简单语言 | 解释器模式 |
| 需要遍历集合 | 迭代器模式 |
| 需要解耦对象交互 | 中介者模式 |
| 需要保存恢复状态 | 备忘录模式 |
| 需要事件通知 | 观察者模式 |
| 需要状态驱动行为 | 状态模式 |
| 需要动态选择算法 | 策略模式 |
| 需要定义算法骨架 | 模板方法 |
| 需要操作对象结构 | 访问者模式 |
# 最佳实践
-
理解问题再选择模式
- 先分析问题本质
- 确定是否真的需要设计模式
- 选择最适合的模式
-
保持简单
- 不要为了使用模式而使用
- 简单的解决方案通常更好
- 考虑维护成本
-
组合使用模式
- 多个模式可以协同工作
- 注意模式间的依赖关系
- 避免过度设计
-
团队协作
- 确保团队成员理解设计
- 统一编码规范
- 定期代码审查
# 总结
设计模式是软件开发中的重要工具,它们提供了经过验证的解决方案来解决常见的设计问题。通过学习和应用这些模式,我们可以:
# 学到的技能
- 编写更可维护、可扩展的代码
- 提高代码质量和可读性
- 更好地理解面向对象设计原则
- 在团队中建立共同的设计语言
# 学习建议
- 循序渐进 - 从简单的模式开始,逐步学习复杂的模式
- 实践应用 - 在实际项目中尝试应用学到的模式
- 理解原理 - 不仅要学会使用,更要理解背后的设计思想
- 持续学习 - 设计模式是一个持续学习的过程
# 未来展望
- 学习更多的架构模式
- 了解领域驱动设计 (DDD)
- 掌握函数式编程模式
- 关注新兴的设计思想和模式
正确地使用设计模式可以帮助我们构建更好的软件系统。记住,最好的设计是最简单、最清晰的设计。
# 参考资料
- 《设计模式:可复用面向对象软件的基础》 - Gang of Four
- 《Head First 设计模式》 - Eric Freeman & Elisabeth Robson
- 《大话设计模式》 - 程杰
- Refactoring.Guru - https://refactoring.guru/design-patterns
- SourceMaking - https://sourcemaking.com/design_patterns
希望这份指南能帮助你更好地理解和应用设计模式! Happy Coding! 🚀