1. 创建型设计模式概述
在C++开发中,创建型设计模式是解决对象创建问题的经典方案。它们通过封装对象创建过程,将创建逻辑与使用逻辑分离,从而提升代码的灵活性和可维护性。创建型模式主要包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式五种。
为什么需要创建型模式?直接使用new关键字创建对象看似简单,但在复杂系统中会导致以下问题:
- 对象创建逻辑分散在代码各处,难以维护
- 客户端与具体类强耦合,修改实现类需要改动大量代码
- 无法灵活控制对象的生命周期和创建方式
2. 单例模式深度解析
2.1 单例模式的核心思想
单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式特别适合以下场景:
- 需要全局唯一的管理类(如配置管理器、日志系统)
- 对象创建成本高(如数据库连接池)
- 需要严格控制实例数量(如线程池)
2.1.1 基础实现要点
一个标准的单例模式实现需要包含:
- 私有构造函数(禁止外部实例化)
- 私有静态成员变量(存储唯一实例)
- 公有静态方法(提供全局访问点)
cpp复制class Singleton {
private:
static Singleton* instance;
Singleton() {} // 私有构造函数
public:
static Singleton* GetInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
// 静态成员初始化
Singleton* Singleton::instance = nullptr;
2.2 线程安全实现方案
2.2.1 双重检查锁定模式(DCLP)
基础实现存在线程安全问题,多个线程可能同时创建实例。解决方案是使用互斥锁+双重检查:
cpp复制#include <mutex>
class ThreadSafeSingleton {
private:
static ThreadSafeSingleton* instance;
static std::mutex mtx;
ThreadSafeSingleton() {}
public:
static ThreadSafeSingleton* GetInstance() {
if (instance == nullptr) { // 第一次检查(无锁)
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) { // 第二次检查(加锁后)
instance = new ThreadSafeSingleton();
}
}
return instance;
}
};
2.2.2 饿汉式实现
另一种线程安全方案是饿汉式,在程序启动时就创建实例:
cpp复制class EagerSingleton {
private:
static EagerSingleton instance;
EagerSingleton() {}
public:
static EagerSingleton& GetInstance() {
return instance;
}
};
// 程序启动时初始化
EagerSingleton EagerSingleton::instance;
2.3 单例模式实践:日志系统
下面是一个完整的线程安全日志系统实现:
cpp复制#include <iostream>
#include <fstream>
#include <mutex>
#include <ctime>
enum class LogLevel { DEBUG, INFO, WARNING, ERROR };
class Logger {
private:
static Logger* instance;
static std::mutex mtx;
std::ofstream logFile;
Logger() {
logFile.open("app.log", std::ios::app);
}
std::string GetTimeStamp() {
time_t now = time(nullptr);
char buf[80];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
return buf;
}
public:
static Logger* GetInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Logger();
}
}
return instance;
}
void Log(LogLevel level, const std::string& message) {
std::lock_guard<std::mutex> lock(mtx);
std::string levelStr;
switch (level) {
case LogLevel::DEBUG: levelStr = "DEBUG"; break;
case LogLevel::INFO: levelStr = "INFO"; break;
case LogLevel::WARNING: levelStr = "WARNING"; break;
case LogLevel::ERROR: levelStr = "ERROR"; break;
}
std::string logEntry = "[" + GetTimeStamp() + "] [" + levelStr + "] " + message;
std::cout << logEntry << std::endl;
logFile << logEntry << std::endl;
}
~Logger() {
if (logFile.is_open()) {
logFile.close();
}
}
};
// 静态成员初始化
Logger* Logger::instance = nullptr;
std::mutex Logger::mtx;
3. 工厂方法模式详解
3.1 工厂方法模式的核心思想
工厂方法模式定义了一个创建对象的接口,但让子类决定实例化哪个类。这种模式将对象的创建延迟到子类,实现了创建逻辑与使用逻辑的解耦。
3.1.1 模式结构
工厂方法模式包含以下角色:
- 抽象产品(Product):定义产品的接口
- 具体产品(ConcreteProduct):实现抽象产品接口
- 抽象工厂(Creator):声明工厂方法
- 具体工厂(ConcreteCreator):实现工厂方法,返回具体产品实例
3.2 C++实现示例
3.2.1 文档处理系统
假设我们需要开发一个文档处理系统,支持多种文档格式:
cpp复制// 抽象产品
class Document {
public:
virtual void Open() = 0;
virtual void Save() = 0;
virtual ~Document() {}
};
// 具体产品
class WordDocument : public Document {
public:
void Open() override { std::cout << "Opening Word document" << std::endl; }
void Save() override { std::cout << "Saving Word document" << std::endl; }
};
class PdfDocument : public Document {
public:
void Open() override { std::cout << "Opening PDF document" << std::endl; }
void Save() override { std::cout << "Saving PDF document" << std::endl; }
};
// 抽象工厂
class DocumentCreator {
public:
virtual Document* CreateDocument() = 0;
virtual ~DocumentCreator() {}
};
// 具体工厂
class WordDocumentCreator : public DocumentCreator {
public:
Document* CreateDocument() override { return new WordDocument(); }
};
class PdfDocumentCreator : public DocumentCreator {
public:
Document* CreateDocument() override { return new PdfDocument(); }
};
3.2.2 客户端使用
cpp复制void ProcessDocument(DocumentCreator& creator) {
Document* doc = creator.CreateDocument();
doc->Open();
doc->Save();
delete doc;
}
int main() {
WordDocumentCreator wordCreator;
PdfDocumentCreator pdfCreator;
ProcessDocument(wordCreator);
ProcessDocument(pdfCreator);
return 0;
}
3.3 工厂方法模式的优势
- 符合开闭原则:新增产品类型时只需添加新的工厂类,无需修改现有代码
- 解耦客户端与具体产品:客户端只依赖抽象接口
- 代码可维护性高:创建逻辑集中在工厂类中
4. 抽象工厂模式深入探讨
4.1 抽象工厂模式的核心思想
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要指定具体类。与工厂方法模式不同,抽象工厂模式关注的是产品家族的创建。
4.1.1 模式结构
抽象工厂模式包含:
- 抽象工厂(AbstractFactory):声明创建产品家族的方法
- 具体工厂(ConcreteFactory):实现创建方法,生产具体产品
- 抽象产品(AbstractProduct):定义产品接口
- 具体产品(ConcreteProduct):实现产品接口
4.2 GUI跨平台实现示例
4.2.1 定义抽象产品
cpp复制// 按钮接口
class Button {
public:
virtual void Render() = 0;
virtual ~Button() {}
};
// 复选框接口
class CheckBox {
public:
virtual void Render() = 0;
virtual ~CheckBox() {}
};
4.2.2 具体产品实现
cpp复制// Windows风格产品
class WindowsButton : public Button {
public:
void Render() override { std::cout << "Rendering Windows button" << std::endl; }
};
class WindowsCheckBox : public CheckBox {
public:
void Render() override { std::cout << "Rendering Windows checkbox" << std::endl; }
};
// Mac风格产品
class MacButton : public Button {
public:
void Render() override { std::cout << "Rendering Mac button" << std::endl; }
};
class MacCheckBox : public CheckBox {
public:
void Render() override { std::cout << "Rendering Mac checkbox" << std::endl; }
};
4.2.3 抽象工厂实现
cpp复制class GUIFactory {
public:
virtual Button* CreateButton() = 0;
virtual CheckBox* CreateCheckBox() = 0;
virtual ~GUIFactory() {}
};
// Windows工厂
class WindowsFactory : public GUIFactory {
public:
Button* CreateButton() override { return new WindowsButton(); }
CheckBox* CreateCheckBox() override { return new WindowsCheckBox(); }
};
// Mac工厂
class MacFactory : public GUIFactory {
public:
Button* CreateButton() override { return new MacButton(); }
CheckBox* CreateCheckBox() override { return new MacCheckBox(); }
};
4.3 客户端代码
cpp复制void CreateUI(GUIFactory& factory) {
Button* button = factory.CreateButton();
CheckBox* checkbox = factory.CreateCheckBox();
button->Render();
checkbox->Render();
delete button;
delete checkbox;
}
int main() {
// 创建Windows风格UI
WindowsFactory winFactory;
CreateUI(winFactory);
// 创建Mac风格UI
MacFactory macFactory;
CreateUI(macFactory);
return 0;
}
5. 创建型模式对比与选择指南
5.1 模式对比分析
| 模式 | 关注点 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 单例 | 对象唯一性 | 全局访问点、资源共享 | 节省资源、统一管理 | 测试困难、违反单一职责 |
| 工厂方法 | 单个产品创建 | 需要扩展产品类型 | 符合开闭原则、解耦 | 每个产品需要对应工厂 |
| 抽象工厂 | 产品家族创建 | 相关对象集合 | 保证产品兼容性、易于切换产品族 | 扩展新产品困难 |
5.2 选择建议
- 需要确保全局唯一实例 → 单例模式
- 需要灵活创建单个产品 → 工厂方法模式
- 需要创建一组相关产品 → 抽象工厂模式
实际项目中,这些模式经常组合使用。例如,抽象工厂中的具体工厂可能使用单例模式实现。
6. 实战经验与注意事项
6.1 单例模式实践要点
- 线程安全是首要考虑因素
- 考虑延迟初始化与性能的平衡
- 注意单例对象的生命周期管理
- 避免在单例中保存过多状态
6.2 工厂模式使用技巧
- 当产品创建逻辑复杂时,考虑使用工厂
- 优先使用简单工厂或工厂方法,必要时才用抽象工厂
- 可以通过配置文件决定使用哪个具体工厂
- 工厂对象本身通常应该是无状态的
6.3 常见问题解决方案
问题1:单例模式如何实现按需销毁?
解决方案:使用智能指针管理实例:
cpp复制class ManagedSingleton {
private:
static std::shared_ptr<ManagedSingleton> instance;
static std::mutex mtx;
ManagedSingleton() {}
public:
static std::shared_ptr<ManagedSingleton> GetInstance() {
if (!instance) {
std::lock_guard<std::mutex> lock(mtx);
if (!instance) {
instance.reset(new ManagedSingleton());
}
}
return instance;
}
static void DestroyInstance() {
std::lock_guard<std::mutex> lock(mtx);
instance.reset();
}
};
问题2:如何扩展抽象工厂?
解决方案:使用注册表模式:
cpp复制class GUIFactoryRegistry {
private:
static std::map<std::string, std::function<GUIFactory*()>> factories;
public:
static void RegisterFactory(const std::string& name, std::function<GUIFactory*()> creator) {
factories[name] = creator;
}
static GUIFactory* CreateFactory(const std::string& name) {
auto it = factories.find(name);
if (it != factories.end()) {
return it->second();
}
return nullptr;
}
};
7. 性能考量与优化
7.1 单例模式性能优化
- 无锁实现(C++11后的magic static):
cpp复制class OptimizedSingleton {
public:
static OptimizedSingleton& GetInstance() {
static OptimizedSingleton instance;
return instance;
}
};
- 减少锁竞争:使用原子操作或双重检查锁定
7.2 工厂模式性能考量
- 工厂对象通常只需创建一次,可以缓存
- 考虑使用对象池管理频繁创建销毁的产品对象
- 简单工厂比多态工厂性能更好
8. 测试策略
8.1 单例模式测试
- 使用依赖注入替换单例进行测试
- 为单例创建测试专用子类
- 提供重置方法用于测试清理
8.2 工厂模式测试
- 测试每个工厂创建的产品类型是否正确
- 验证产品接口的一致性
- 模拟异常情况测试工厂的健壮性
9. 现代C++特性应用
9.1 使用智能指针
cpp复制std::unique_ptr<Document> DocumentCreator::CreateDocument() {
return std::make_unique<WordDocument>();
}
9.2 使用模板实现通用工厂
cpp复制template <typename Product>
class GenericFactory {
public:
virtual std::unique_ptr<Product> Create() = 0;
virtual ~GenericFactory() = default;
};
10. 设计模式组合应用
10.1 单例+抽象工厂
cpp复制class ThemeManager {
private:
static ThemeManager* instance;
GUIFactory* currentFactory;
ThemeManager() : currentFactory(new WindowsFactory()) {}
public:
static ThemeManager* GetInstance() {
if (!instance) {
instance = new ThemeManager();
}
return instance;
}
void SetTheme(const std::string& theme) {
delete currentFactory;
if (theme == "Windows") {
currentFactory = new WindowsFactory();
} else {
currentFactory = new MacFactory();
}
}
GUIFactory* GetCurrentFactory() {
return currentFactory;
}
};
10.2 工厂方法+原型模式
cpp复制class PrototypeDocument : public Document {
public:
virtual PrototypeDocument* Clone() = 0;
};
class DocumentCreator {
public:
virtual PrototypeDocument* CreateDocument() = 0;
PrototypeDocument* CreateDocumentFromTemplate(PrototypeDocument* proto) {
return proto->Clone();
}
};
在实际项目中,我经常发现开发者过度使用单例模式。单例确实方便,但它本质上是一个全局变量,会带来测试困难和隐藏的依赖关系。我的建议是:只有在真正需要强制唯一实例时才使用单例,其他情况下考虑依赖注入。
对于工厂模式,一个实用的技巧是为工厂接口添加参数化创建方法,这样可以在不改变接口的情况下扩展创建逻辑。例如,DocumentCreator可以增加CreateDocumentFromFile(path)方法,而具体工厂可以决定如何解析文件创建文档对象。