1. C++设计模式概述
设计模式是软件工程中解决常见问题的可复用方案模板,它们不是可以直接转换为代码的完整设计,而是解决特定问题的描述或模板。在C++这种强类型、高性能的系统编程语言中,设计模式的应用尤为重要,因为它们能帮助开发者构建更灵活、可维护的代码结构。
我第一次接触设计模式是在一个大型金融交易系统项目中,当时系统因为缺乏良好的架构设计而变得难以维护。引入适当的设计模式后,代码的可读性和扩展性得到了显著提升。这也是为什么我认为每个C++开发者都应该深入理解这些模式。
2. 创建型模式解析
2.1 单例模式(Singleton)
单例模式确保一个类只有一个实例,并提供一个全局访问点。在C++中实现单例需要注意线程安全和资源释放问题。
cpp复制class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {} // 私有构造函数
public:
Singleton(const Singleton&) = delete; // 删除拷贝构造函数
Singleton& operator=(const Singleton&) = delete; // 删除赋值运算符
static Singleton* getInstance() {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
~Singleton() {
// 清理资源
}
};
// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
注意:现代C++(C++11及以上)中,可以使用Meyer's Singleton实现更简洁的线程安全单例:
cpp复制Singleton& Singleton::getInstance() { static Singleton instance; return instance; }
2.2 工厂方法模式(Factory Method)
工厂方法定义了一个创建对象的接口,但让子类决定实例化哪个类。这种模式在需要创建一系列相关对象时特别有用。
cpp复制class Product {
public:
virtual ~Product() {}
virtual void operation() = 0;
};
class ConcreteProductA : public Product {
public:
void operation() override {
std::cout << "ConcreteProductA operation" << std::endl;
}
};
class Creator {
public:
virtual ~Creator() {}
virtual Product* createProduct() = 0;
void someOperation() {
Product* product = createProduct();
product->operation();
delete product;
}
};
class ConcreteCreatorA : public Creator {
public:
Product* createProduct() override {
return new ConcreteProductA();
}
};
2.3 抽象工厂模式(Abstract Factory)
抽象工厂提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
cpp复制class Button {
public:
virtual void render() = 0;
virtual ~Button() {}
};
class WindowsButton : public Button {
public:
void render() override {
std::cout << "Rendering a Windows style button" << std::endl;
}
};
class GUIFactory {
public:
virtual Button* createButton() = 0;
virtual ~GUIFactory() {}
};
class WindowsFactory : public GUIFactory {
public:
Button* createButton() override {
return new WindowsButton();
}
};
3. 结构型模式详解
3.1 适配器模式(Adapter)
适配器模式允许不兼容的接口一起工作。这在整合第三方库或遗留代码时特别有用。
cpp复制// 目标接口
class Target {
public:
virtual void request() = 0;
virtual ~Target() {}
};
// 需要适配的类
class Adaptee {
public:
void specificRequest() {
std::cout << "Adaptee's specific request" << std::endl;
}
};
// 类适配器(通过多重继承)
class ClassAdapter : public Target, private Adaptee {
public:
void request() override {
specificRequest();
}
};
// 对象适配器(通过组合)
class ObjectAdapter : public Target {
private:
Adaptee* adaptee;
public:
ObjectAdapter(Adaptee* a) : adaptee(a) {}
void request() override {
adaptee->specificRequest();
}
~ObjectAdapter() {
delete adaptee;
}
};
3.2 装饰器模式(Decorator)
装饰器模式动态地给对象添加额外的职责,比继承更灵活。
cpp复制class Component {
public:
virtual void operation() = 0;
virtual ~Component() {}
};
class ConcreteComponent : public Component {
public:
void operation() override {
std::cout << "ConcreteComponent operation" << std::endl;
}
};
class Decorator : public Component {
protected:
Component* component;
public:
Decorator(Component* c) : component(c) {}
void operation() override {
if (component) {
component->operation();
}
}
~Decorator() {
delete component;
}
};
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(Component* c) : Decorator(c) {}
void operation() override {
Decorator::operation();
addedBehavior();
}
private:
void addedBehavior() {
std::cout << "Added behavior from ConcreteDecoratorA" << std::endl;
}
};
4. 行为型模式实践
4.1 观察者模式(Observer)
观察者模式定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知并自动更新。
cpp复制#include <vector>
#include <algorithm>
class Observer {
public:
virtual void update(float temp, float humidity, float pressure) = 0;
virtual ~Observer() {}
};
class Subject {
private:
std::vector<Observer*> observers;
public:
void registerObserver(Observer* o) {
observers.push_back(o);
}
void removeObserver(Observer* o) {
observers.erase(std::remove(observers.begin(), observers.end(), o), observers.end());
}
void notifyObservers(float temp, float humidity, float pressure) {
for (Observer* observer : observers) {
observer->update(temp, humidity, pressure);
}
}
};
class WeatherData : public Subject {
private:
float temperature;
float humidity;
float pressure;
public:
void measurementsChanged() {
notifyObservers(temperature, humidity, pressure);
}
void setMeasurements(float temp, float humidity, float pressure) {
this->temperature = temp;
this->humidity = humidity;
this->pressure = pressure;
measurementsChanged();
}
};
class CurrentConditionsDisplay : public Observer {
private:
float temperature;
float humidity;
Subject* weatherData;
public:
CurrentConditionsDisplay(Subject* wd) : weatherData(wd) {
weatherData->registerObserver(this);
}
void update(float temp, float humidity, float pressure) override {
this->temperature = temp;
this->humidity = humidity;
display();
}
void display() {
std::cout << "Current conditions: " << temperature
<< "F degrees and " << humidity << "% humidity" << std::endl;
}
~CurrentConditionsDisplay() {
weatherData->removeObserver(this);
}
};
4.2 策略模式(Strategy)
策略模式定义一系列算法,封装每个算法,并使它们可以互换。策略模式让算法独立于使用它的客户端而变化。
cpp复制class SortingStrategy {
public:
virtual void sort(std::vector<int>& data) = 0;
virtual ~SortingStrategy() {}
};
class BubbleSort : public SortingStrategy {
public:
void sort(std::vector<int>& data) override {
std::cout << "Sorting using bubble sort" << std::endl;
// 实际冒泡排序实现
for (size_t i = 0; i < data.size(); ++i) {
for (size_t j = 0; j < data.size() - i - 1; ++j) {
if (data[j] > data[j+1]) {
std::swap(data[j], data[j+1]);
}
}
}
}
};
class QuickSort : public SortingStrategy {
public:
void sort(std::vector<int>& data) override {
std::cout << "Sorting using quick sort" << std::endl;
quickSort(data, 0, data.size() - 1);
}
private:
void quickSort(std::vector<int>& data, int low, int high) {
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) {
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;
}
};
class Sorter {
private:
SortingStrategy* strategy;
public:
Sorter(SortingStrategy* s) : strategy(s) {}
void setStrategy(SortingStrategy* s) {
strategy = s;
}
void sort(std::vector<int>& data) {
strategy->sort(data);
}
~Sorter() {
delete strategy;
}
};
5. 设计模式应用实践与常见问题
5.1 何时使用何种模式
在实际项目中,选择合适的设计模式需要考虑多个因素:
-
创建型模式选择指南:
- 需要严格控制实例数量时 → 单例模式
- 需要创建复杂对象时 → 生成器模式
- 需要创建相关对象家族时 → 抽象工厂模式
- 需要避免直接依赖具体类时 → 工厂方法模式
-
结构型模式选择指南:
- 接口不匹配需要适配时 → 适配器模式
- 需要动态添加功能时 → 装饰器模式
- 需要处理树形结构时 → 组合模式
- 需要简化复杂系统接口时 → 外观模式
-
行为型模式选择指南:
- 对象间存在一对多依赖时 → 观察者模式
- 需要封装算法时 → 策略模式
- 需要处理对象状态变化时 → 状态模式
- 需要处理请求链时 → 责任链模式
5.2 C++实现设计模式的注意事项
-
内存管理:C++没有垃圾回收,使用设计模式时要特别注意对象的生命周期。智能指针(std::unique_ptr, std::shared_ptr)可以大大简化内存管理。
-
多线程安全:许多设计模式需要考虑线程安全问题,特别是单例模式、观察者模式等。
-
性能考虑:某些模式(如装饰器模式)会增加间接层,可能影响性能。在性能关键代码中需要权衡。
-
过度设计风险:不是所有情况都需要使用设计模式。简单的代码往往比"模式化"的复杂代码更好。
5.3 常见问题与解决方案
问题1:单例模式在多线程环境下可能创建多个实例。
解决方案:
- 使用C++11的局部静态变量(Meyer's Singleton)
- 使用双重检查锁定模式(需注意内存屏障)
- 使用std::call_once
问题2:观察者模式中的内存泄漏。
解决方案:
- 使用weak_ptr打破循环引用
- 明确的生命周期管理
- 在观察者析构时自动取消注册
问题3:工厂方法导致类爆炸。
解决方案:
- 结合模板减少子类数量
- 使用参数化工厂方法
- 在简单情况下考虑使用简单工厂而非工厂方法
6. 现代C++中的设计模式实现
现代C++(C++11/14/17/20)提供了许多新特性,可以更优雅地实现设计模式:
6.1 使用lambda实现策略模式
cpp复制class Sorter {
public:
void sort(std::vector<int>& data, std::function<void(std::vector<int>&)> strategy) {
strategy(data);
}
};
// 使用
Sorter sorter;
sorter.sort(data, [](std::vector<int>& d) {
std::sort(d.begin(), d.end());
});
6.2 使用std::variant实现访问者模式
cpp复制using Shape = std::variant<Circle, Square>;
class DrawVisitor {
public:
void operator()(const Circle& c) const {
// 绘制圆形
}
void operator()(const Square& s) const {
// 绘制方形
}
};
Shape shape = Circle{5.0};
std::visit(DrawVisitor{}, shape);
6.3 使用智能指针管理对象关系
cpp复制// 观察者模式中使用weak_ptr
class Subject {
private:
std::vector<std::weak_ptr<Observer>> observers;
void notifyObservers() {
for (auto& weak_obs : observers) {
if (auto obs = weak_obs.lock()) {
obs->update();
}
}
}
};
class Observer : public std::enable_shared_from_this<Observer> {
public:
void registerWith(Subject& subject) {
subject.registerObserver(shared_from_this());
}
};
在实际项目中,我发现设计模式不是银弹,但它们确实提供了经过验证的解决方案框架。关键是要理解每个模式解决的问题和适用场景,而不是机械地套用。C++的强大表达能力让我们可以灵活地实现这些模式,但也要注意不要过度设计。
