跳到主要内容
C++ 工厂模式与单例模式深度应用 | 极客日志
C++
C++ 工厂模式与单例模式深度应用 C++ 工厂模式与单例模式是创建型设计模式的核心。工厂模式包括简单工厂、工厂方法和抽象工厂,用于解耦对象创建与使用,支持扩展性。单例模式确保全局唯一实例,分为饿汉式和懒汉式,需处理线程安全。文章通过计算器、日志系统、跨平台 UI 等案例展示实现细节,结合配置管理解决实际问题,强调避免过度设计和并发陷阱。
云朵棉花糖 发布于 2026/2/9 更新于 2026/5/27 20 浏览第 17 章 设计模式实战:C++ 中工厂模式与单例模式的深度应用
17.1 本章学习目标与重点
💡 掌握工厂模式(简单工厂、工厂方法、抽象工厂)的核心设计思想、适用场景及 C++ 实现细节
💡 理解单例模式的两种核心实现(饿汉式、懒汉式)及线程安全优化方案
💡 能够结合实际开发场景(如组件创建、配置管理)灵活选择设计模式
💡 规避设计模式使用中的常见陷阱(如过度设计、线程安全问题)
重点:工厂模式的层次演进逻辑、单例模式的线程安全实现、设计模式在项目中的落地技巧
17.2 设计模式基础认知
在 C++ 开发中,'设计模式'是前人总结的代码设计经验模板,用于解决特定场景下的代码复用、扩展性、维护性问题。设计模式不直接提供可运行的代码,而是提供一套抽象的设计思路,帮助我们编写'高内聚、低耦合'的优质代码。
17.2.1 为什么需要设计模式?
实际开发中,我们常遇到以下问题:
新增功能时,需要修改大量现有代码(如新增产品类型时,到处修改 if-else 判断);
代码耦合严重,一个模块的修改会影响多个其他模块;
组件创建逻辑复杂,分散在代码各处,难以维护;
多人协作时,代码风格不统一,扩展性差。
设计模式通过标准化的设计思路,将'变化的部分'与'不变的部分'分离,例如工厂模式负责统一管理对象创建,单例模式确保全局唯一实例,从而解决上述问题。
17.2.2 设计模式的核心原则
在学习具体模式前,需牢记以下核心原则(为后续模式应用奠定基础):
单一职责原则:一个类只负责一项功能(如工厂类只负责创建对象,不负责对象的业务逻辑);
开放 - 封闭原则:对扩展开放,对修改关闭(如新增产品时,无需修改工厂核心代码);
依赖倒置原则:依赖抽象,不依赖具体实现(如工厂依赖产品抽象类,而非具体产品类);
接口隔离原则:使用多个专门的接口,而非一个统一的接口。
17.3 工厂模式:对象创建的标准化解决方案
工厂模式是创建型设计模式的核心,其核心思想是'将对象的创建与使用分离',由专门的工厂类负责对象创建,使用者无需关心对象的创建细节(如构造函数参数、初始化流程)。
根据复杂度和扩展性,工厂模式分为三个层次:简单工厂模式 、工厂方法模式 、抽象工厂模式 。
17.3.1 简单工厂模式:基础对象创建器
1. 核心定义
简单工厂模式(Simple Factory):定义一个工厂类,根据传入的参数,动态创建不同产品类的实例(所有产品都继承自同一个抽象基类)。
2. 适用场景
产品类型较少(通常不超过 5 种);
产品创建逻辑简单,且变化频率低;
使用者无需关心产品创建细节,只需通过参数指定产品类型。
3. C++ 实现示例:计算器运算器
假设我们需要开发一个简单计算器,支持加法、减法、乘法、除法运算。若直接在主函数中创建运算对象,会导致 if-else 冗余且扩展性差,使用简单工厂模式可解决该问题。
步骤 1:定义产品抽象基类
首先定义所有运算的抽象基类 Operation,声明统一的接口 GetResult():
#include
std;
{
:
numA;
numB;
= ;
~ () {}
};
<iostream>
#include <string>
using
namespace
class
Operation
public
double
double
virtual double GetResult () const
0
virtual
Operation
步骤 2:实现具体产品类 为每种运算实现具体产品类,继承自 Operation 并实现 GetResult():
class OperationAdd : public Operation {
public :
double GetResult () const override {
return numA + numB;
}
};
class OperationSub : public Operation {
public :
double GetResult () const override {
return numA - numB;
}
};
class OperationMul : public Operation {
public :
double GetResult () const override {
return numA * numB;
}
};
class OperationDiv : public Operation {
public :
double GetResult () const override {
if (numB == 0 ) {
throw runtime_error ("除数不能为 0!" );
}
return numA / numB;
}
};
步骤 3:实现简单工厂类 创建工厂类 OperationFactory,提供静态方法 CreateOperation(),根据传入的运算符参数创建对应的运算对象:
class OperationFactory {
public :
static Operation* CreateOperation (char op) {
Operation* operation = nullptr ;
switch (op) {
case '+' : operation = new OperationAdd (); break ;
case '-' : operation = new OperationSub (); break ;
case '*' : operation = new OperationMul (); break ;
case '/' : operation = new OperationDiv (); break ;
default : throw invalid_argument ("不支持的运算符!" );
}
return operation;
}
};
步骤 4:客户端使用示例 客户端只需调用工厂类的静态方法,传入参数即可获取对象,无需关心对象创建细节:
int main () {
try {
Operation* addOp = OperationFactory::CreateOperation ('+' );
addOp->numA = 10 ;
addOp->numB = 20 ;
cout << "10 + 20 = " << addOp->GetResult () << endl;
delete addOp;
Operation* divOp = OperationFactory::CreateOperation ('/' );
divOp->numA = 50 ;
divOp->numB = 5 ;
cout << "50 / 5 = " << divOp->GetResult () << endl;
delete divOp;
Operation* errDivOp = OperationFactory::CreateOperation ('/' );
errDivOp->numA = 30 ;
errDivOp->numB = 0 ;
cout << "30 / 0 = " << errDivOp->GetResult () << endl;
delete errDivOp;
} catch (const exception& e) {
cout << "错误:" << e.what () << endl;
}
return 0 ;
}
4. 简单工厂模式的优缺点
实现对象创建与使用分离,客户端代码简洁,无需关心创建细节;
集中管理对象创建逻辑,便于维护。
违反'开放 - 封闭原则':新增产品时,需修改工厂类的 switch 逻辑;
工厂类职责过重,若产品类型过多,会导致工厂类代码臃肿(如支持 10 种运算时,switch 会非常长)。
17.3.2 工厂方法模式:解决简单工厂的扩展性问题
1. 核心定义 工厂方法模式(Factory Method):定义一个创建对象的接口(抽象工厂),让子类决定实例化哪个产品类。即'将工厂的创建逻辑延迟到子类',每个产品对应一个专属工厂。
2. 适用场景
产品类型较多,且未来可能持续新增;
希望遵守'开放 - 封闭原则',新增产品时无需修改现有代码;
产品创建逻辑复杂,不同产品的创建流程差异较大。
3. C++ 实现示例:日志系统 假设我们需要开发一个日志系统,支持文件日志(写入文件)、控制台日志(打印到控制台)、数据库日志(存入数据库),且未来可能新增'网络日志'。使用工厂方法模式,可实现无侵入式扩展。
步骤 1:定义产品抽象基类 #include <iostream>
#include <string>
#include <fstream>
using namespace std;
class Logger {
public :
virtual void WriteLog (const string& message) = 0 ;
virtual ~Logger () {}
};
步骤 2:实现具体产品类
class ConsoleLogger : public Logger {
public :
void WriteLog (const string& message) override {
cout << "[控制台日志] " << message << endl;
}
};
class FileLogger : public Logger {
public :
void WriteLog (const string& message) override {
ofstream logFile ("log.txt" , ios::app) ;
if (logFile.is_open ()) {
logFile << "[文件日志] " << message << endl;
logFile.close ();
} else {
throw runtime_error ("无法打开日志文件!" );
}
}
};
class DatabaseLogger : public Logger {
public :
void WriteLog (const string& message) override {
cout << "[数据库日志] 连接数据库成功,写入日志:" << message << endl;
}
};
步骤 3:定义抽象工厂基类
class LoggerFactory {
public :
virtual Logger* CreateLogger () = 0 ;
virtual ~LoggerFactory () {}
};
步骤 4:实现具体工厂类 每个产品对应一个专属工厂,实现 CreateLogger() 方法:
class ConsoleLoggerFactory : public LoggerFactory {
public :
Logger* CreateLogger () override {
return new ConsoleLogger ();
}
};
class FileLoggerFactory : public LoggerFactory {
public :
Logger* CreateLogger () override {
return new FileLogger ();
}
};
class DatabaseLoggerFactory : public LoggerFactory {
public :
Logger* CreateLogger () override {
return new DatabaseLogger ();
}
};
步骤 5:客户端使用示例 客户端根据需求选择对应的工厂,再通过工厂创建产品:
int main () {
try {
LoggerFactory* consoleFactory = new ConsoleLoggerFactory ();
Logger* consoleLogger = consoleFactory->CreateLogger ();
consoleLogger->WriteLog ("用户登录成功" );
delete consoleLogger;
delete consoleFactory;
LoggerFactory* fileFactory = new FileLoggerFactory ();
Logger* fileLogger = fileFactory->CreateLogger ();
fileLogger->WriteLog ("系统启动完成" );
delete fileLogger;
delete fileFactory;
LoggerFactory* dbFactory = new DatabaseLoggerFactory ();
Logger* dbLogger = dbFactory->CreateLogger ();
dbLogger->WriteLog ("数据备份成功" );
delete dbLogger;
delete dbFactory;
} catch (const exception& e) {
cout << "日志写入失败:" << e.what () << endl;
}
return 0 ;
}
4. 工厂方法模式的优缺点
完全遵守'开放 - 封闭原则':新增产品时,只需新增产品类和对应工厂类,无需修改现有代码;
工厂职责单一,每个工厂只负责创建一种产品,代码结构清晰;
扩展性强,支持不同产品的创建逻辑差异化。
类数量爆炸:每增加一个产品,需同时新增产品类和工厂类,增加代码复杂度;
客户端需要知道具体工厂的存在(如 ConsoleLoggerFactory),增加了客户端的学习成本。
17.3.3 抽象工厂模式:多产品族的创建解决方案
1. 核心定义 抽象工厂模式(Abstract Factory):提供一个接口,用于创建一系列相关或相互依赖的对象(产品族),而无需指定它们的具体类。
2. 关键概念
产品族:一组相关的产品(如'Windows 系统的按钮、文本框、下拉框'属于一个产品族,'Mac 系统的按钮、文本框、下拉框'属于另一个产品族);
产品等级结构:同一类产品的不同实现(如'按钮'是一个产品等级结构,包含 Windows 按钮、Mac 按钮)。
简单来说,工厂方法模式解决'同一产品等级结构'的扩展问题,抽象工厂模式解决'同一产品族'的创建问题 。
3. 适用场景
系统需要支持多个产品族(如跨平台 UI 组件、不同数据库的驱动套件);
产品族中的产品相互依赖,需要统一创建和管理;
希望客户端能统一使用多个相关产品,而无需关心它们的具体实现。
4. C++ 实现示例:跨平台 UI 组件库 假设我们需要开发一个跨平台 UI 库,支持 Windows 和 Mac 两个平台,每个平台包含'按钮(Button)'和'文本框(TextBox)'两个组件(即两个产品族:Windows 产品族、Mac 产品族)。
步骤 1:定义产品族的抽象基类 #include <iostream>
#include <string>
using namespace std;
class Button {
public :
virtual void Display () = 0 ;
virtual ~Button () {}
};
class TextBox {
public :
virtual void Display () = 0 ;
virtual ~TextBox () {}
};
步骤 2:实现具体产品族(Windows 和 Mac)
class WindowsButton : public Button {
public :
void Display () override {
cout << "显示 Windows 风格按钮(蓝色、矩形)" << endl;
}
};
class WindowsTextBox : public TextBox {
public :
void Display () override {
cout << "显示 Windows 风格文本框(白色背景、黑色文字)" << endl;
}
};
class MacButton : public Button {
public :
void Display () override {
cout << "显示 Mac 风格按钮(灰色、圆角)" << endl;
}
};
class MacTextBox : public TextBox {
public :
void Display () override {
cout << "显示 Mac 风格文本框(浅灰色背景、黑色文字)" << endl;
}
};
步骤 3:定义抽象工厂基类(创建产品族)
class UIFactory {
public :
virtual Button* CreateButton () = 0 ;
virtual TextBox* CreateTextBox () = 0 ;
virtual ~UIFactory () {}
};
步骤 4:实现具体工厂类(每个工厂对应一个产品族)
class WindowsUIFactory : public UIFactory {
public :
Button* CreateButton () override { return new WindowsButton (); }
TextBox* CreateTextBox () override { return new WindowsTextBox (); }
};
class MacUIFactory : public UIFactory {
public :
Button* CreateButton () override { return new MacButton (); }
TextBox* CreateTextBox () override { return new MacTextBox (); }
};
步骤 5:客户端使用示例(切换平台只需更换工厂)
void ShowUI (UIFactory* factory) {
Button* button = factory->CreateButton ();
TextBox* textBox = factory->CreateTextBox ();
button->Display ();
textBox->Display ();
delete button;
delete textBox;
delete factory;
}
int main () {
cout << "=== Windows 平台 UI ===" << endl;
ShowUI (new WindowsUIFactory ());
cout << "\n=== Mac 平台 UI ===" << endl;
ShowUI (new MacUIFactory ());
return 0 ;
}
=== Windows 平台 UI ===
显示 Windows 风格按钮(蓝色、矩形)
显示 Windows 风格文本框(白色背景、黑色文字)
=== Mac 平台 UI ===
显示 Mac 风格按钮(灰色、圆角)
显示 Mac 风格文本框(浅灰色背景、黑色文字)
5. 抽象工厂模式的优缺点
统一管理产品族的创建,确保产品族内的产品相互匹配(如 Windows 按钮和 Windows 文本框不会混用);
支持产品族的整体切换(如从 Windows 平台切换到 Mac 平台,只需更换工厂);
遵守'开放 - 封闭原则',新增产品族时无需修改现有代码。
扩展产品等级结构困难:若需在现有产品族中新增产品(如新增'下拉框'),需修改抽象工厂类及所有具体工厂类,违反'开放 - 封闭原则';
类结构复杂,理解和维护成本较高。
17.3.4 三种工厂模式的选择策略 模式类型 核心特点 适用场景 扩展方式 简单工厂模式 一个工厂创建所有产品 产品类型少、变化少 修改工厂类(违反开放 - 封闭) 工厂方法模式 一个产品对应一个工厂 产品类型多、需单独扩展 新增产品类 + 工厂类(无侵入) 抽象工厂模式 一个工厂创建一个产品族 需管理多个相关产品族 新增产品族(无侵入)、扩展产品等级(困难)
💡 实战技巧:大多数场景下,'工厂方法模式'是性价比最高的选择;若需管理相关产品族,再考虑'抽象工厂模式';'简单工厂模式'仅适用于小型、简单的项目。
17.4 单例模式:全局唯一实例的设计方案 单例模式(Singleton)是创建型设计模式的另一个核心,其核心思想是'确保一个类在整个系统中只有一个实例,并提供一个全局访问点'。
17.4.1 为什么需要单例模式?
配置管理类:系统的配置信息(如数据库连接参数、服务器地址)只需加载一次,全局共享;
日志管理器:所有模块的日志都需写入同一个日志文件,避免多实例导致文件冲突;
数据库连接池:连接池是稀缺资源,多实例会导致资源浪费和连接混乱;
缓存管理器:全局缓存需统一读写,确保数据一致性。
资源浪费(如重复创建数据库连接);
数据不一致(如多个配置实例加载不同的配置文件);
并发冲突(如多个日志实例同时写入同一个文件)。
17.4.2 单例模式的核心要求
私有构造函数:禁止外部通过 new 创建实例;
私有拷贝构造函数和赋值运算符:禁止通过拷贝创建新实例;
静态私有实例:存储唯一的实例对象;
静态公有方法:提供全局访问点(如 GetInstance())。
17.4.3 饿汉式单例:预加载实例
1. 核心思想 饿汉式(Hungry Singleton):在程序启动时(类加载阶段)就创建实例,无论是否使用,实例都已存在。
2. C++ 实现示例:配置管理类 #include <iostream>
#include <string>
#include <map>
using namespace std;
class ConfigManager {
private :
ConfigManager () {
LoadConfig ();
cout << "饿汉式单例:配置文件加载完成" << endl;
}
ConfigManager (const ConfigManager&) = delete ;
ConfigManager& operator =(const ConfigManager&) = delete ;
static ConfigManager instance;
map<string, string> configMap;
void LoadConfig () {
configMap["db_host" ] = "127.0.0.1" ;
configMap["db_port" ] = "3306" ;
configMap["db_user" ] = "root" ;
configMap["db_pwd" ] = "123456" ;
}
public :
static ConfigManager& GetInstance () {
return instance;
}
string GetConfig (const string& key) {
auto iter = configMap.find (key);
if (iter != configMap.end ()) {
return iter->second;
}
return "配置不存在" ;
}
};
ConfigManager ConfigManager::instance;
int main () {
ConfigManager& config1 = ConfigManager::GetInstance ();
cout << "数据库地址:" << config1. GetConfig ("db_host" ) << endl;
ConfigManager& config2 = ConfigManager::GetInstance ();
cout << "数据库端口:" << config2. GetConfig ("db_port" ) << endl;
cout << "config1 地址:" << &config1 << endl;
cout << "config2 地址:" << &config2 << endl;
return 0 ;
}
饿汉式单例:配置文件加载完成
数据库地址:127.0.0.1
数据库端口:3306
config1 地址:0x404068
config2 地址:0x404068
3. 饿汉式单例的优缺点
实现简单,无需考虑线程安全问题(静态实例在程序启动时初始化,仅执行一次);
访问速度快,实例已预加载,无需动态创建。
资源浪费:若程序全程未使用该实例,仍会占用内存;
无法延迟初始化:若实例创建成本高(如加载大型配置文件),会延长程序启动时间。
17.4.4 懒汉式单例:延迟加载实例
1. 核心思想 懒汉式(Lazy Singleton):在第一次调用 GetInstance() 时才创建实例,实现'延迟加载',节省内存。
2. 线程安全问题分析 class Singleton {
private :
static Singleton* instance;
Singleton () {}
public :
static Singleton* GetInstance () {
if (instance == nullptr ) {
instance = new Singleton ();
}
return instance;
}
};
Singleton* Singleton::instance = nullptr ;
问题:多线程环境下,若两个线程同时进入 if (instance == nullptr),会导致创建多个实例,违反单例原则。
3. 线程安全的懒汉式实现(C++11 推荐方案) C++11 标准规定:局部静态变量的初始化是线程安全的(在初始化完成前,其他线程会阻塞)。利用这一特性,可实现简洁、高效的线程安全懒汉式单例。
实现示例:日志管理器 #include <iostream>
#include <string>
#include <fstream>
#include <mutex>
#include <thread>
using namespace std;
class LogManager {
private :
LogManager () {
cout << "懒汉式单例:日志管理器初始化" << endl;
}
LogManager (const LogManager&) = delete ;
LogManager& operator =(const LogManager&) = delete ;
string logFilePath = "app.log" ;
public :
static LogManager& GetInstance () {
static LogManager instance;
return instance;
}
void WriteLog (const string& level, const string& message) {
ofstream logFile (logFilePath, ios::app) ;
if (logFile.is_open ()) {
logFile << "[" << level << "] " << message << endl;
logFile.close ();
} else {
throw runtime_error ("日志文件打开失败!" );
}
}
};
void TestThread (int threadId) {
string message = "线程" + to_string (threadId) + "执行日志" ;
LogManager::GetInstance ().WriteLog ("INFO" , message);
}
int main () {
cout << "程序启动,未创建日志实例" << endl;
LogManager::GetInstance ().WriteLog ("INFO" , "系统启动成功" );
thread threads[10 ];
for (int i = 0 ; i < 10 ; ++i) {
threads[i] = thread (TestThread, i);
}
for (int i = 0 ; i < 10 ; ++i) {
threads[i].join ();
}
cout << "日志写入完成,查看 app.log 文件" << endl;
return 0 ;
}
程序启动,未创建日志实例
懒汉式单例:日志管理器初始化
日志写入完成,查看 app.log 文件
[INFO ] 系统启动成功
[INFO ] 线程 0 执行日志
[INFO ] 线程 1 执行日志
...
[INFO ] 线程 9 执行日志
4. 懒汉式单例的优缺点
延迟加载:仅在需要时创建实例,节省内存;
线程安全(C++11 实现):无需手动加锁,依赖标准库保证安全性;
实现简洁,无内存泄漏风险(局部静态变量会自动析构)。
C++11 之前的版本不支持局部静态变量的线程安全,需手动加锁(如使用 std::mutex),但会引入性能开销;
第一次访问时会有初始化开销(影响首次访问速度)。
17.4.5 单例模式的常见陷阱与避坑指南
1. 陷阱 1:拷贝与赋值导致多实例 若未禁用拷贝构造函数和赋值运算符,可能通过以下方式创建新实例:
ConfigManager config3 = ConfigManager::GetInstance ();
✅ 避坑方案:显式删除拷贝构造函数和赋值运算符(C++11 及以上):
ConfigManager (const ConfigManager&) = delete ;
ConfigManager& operator =(const ConfigManager&) = delete ;
2. 陷阱 2:多线程环境下的线程安全问题 naive 懒汉式在多线程下会创建多个实例,即使手动加锁,也可能存在'双重检查锁定'的坑(如指令重排导致的空指针)。
✅ 避坑方案:优先使用 C++11 局部静态变量的实现方式,无需手动处理线程安全。
3. 陷阱 3:析构顺序问题 若单例对象依赖其他全局对象,可能出现'单例已析构,但其他对象仍在使用它'的情况。
尽量避免单例依赖全局对象;
若必须依赖,确保依赖对象的析构顺序在单例之后(可通过局部静态变量的析构顺序保证)。
4. 陷阱 4:过度使用单例模式
代码耦合度高(所有模块依赖单例);
测试困难(单例状态难以模拟和重置)。
✅ 避坑方案:仅在确实需要'全局唯一实例'的场景下使用单例(如配置、日志、连接池),避免将单例作为全局变量的替代品。
17.5 实战案例:结合工厂模式与单例模式开发配置化日志系统
17.5.1 需求分析 开发一个支持多日志类型(控制台、文件、数据库)且可配置的日志系统,要求:
日志类型可通过配置文件动态指定(如配置为'文件日志',则系统自动使用文件日志);
日志系统全局唯一(单例模式),避免多实例冲突;
支持日志类型的扩展(如未来新增'网络日志',无需修改核心代码)。
17.5.2 设计思路
单例模式:日志系统核心管理器(LogSystem)为单例,确保全局唯一;
工厂方法模式:创建日志器(控制台、文件、数据库),支持扩展;
配置驱动:通过单例 ConfigManager 加载日志类型配置,由 LogSystem 自动选择对应的工厂创建日志器。
17.5.3 完整实现代码 #include <iostream>
#include <string>
#include <fstream>
#include <map>
#include <thread>
using namespace std;
class ConfigManager {
private :
ConfigManager () { LoadConfig (); }
ConfigManager (const ConfigManager&) = delete ;
ConfigManager& operator =(const ConfigManager&) = delete ;
static ConfigManager& GetInstance () {
static ConfigManager instance;
return instance;
}
map<string, string> configMap;
void LoadConfig () {
configMap["log_type" ] = "file" ;
configMap["log_file" ] = "system.log" ;
}
public :
static string GetConfig (const string& key) {
return GetInstance ().configMap[key];
}
};
class Logger {
public :
virtual void WriteLog (const string& level, const string& message) = 0 ;
virtual ~Logger () {}
};
class ConsoleLogger : public Logger {
public :
void WriteLog (const string& level, const string& message) override {
cout << "[" << level << "] [控制台] " << message << endl;
}
};
class FileLogger : public Logger {
private :
string logFile;
public :
FileLogger (const string& filePath) : logFile (filePath) {}
void WriteLog (const string& level, const string& message) override {
ofstream file (logFile, ios::app) ;
if (file.is_open ()) {
file << "[" << level << "] [文件] " << message << endl;
file.close ();
} else {
throw runtime_error ("无法打开日志文件:" + logFile);
}
}
};
class DatabaseLogger : public Logger {
public :
void WriteLog (const string& level, const string& message) override {
cout << "[" << level << "] [数据库] " << message << endl;
}
};
class LoggerFactory {
public :
virtual Logger* CreateLogger () = 0 ;
virtual ~LoggerFactory () {}
};
class ConsoleLoggerFactory : public LoggerFactory {
public :
Logger* CreateLogger () override { return new ConsoleLogger (); }
};
class FileLoggerFactory : public LoggerFactory {
public :
Logger* CreateLogger () override {
string logFile = ConfigManager::GetConfig ("log_file" );
return new FileLogger (logFile);
}
};
class DatabaseLoggerFactory : public LoggerFactory {
public :
Logger* CreateLogger () override { return new DatabaseLogger (); }
};
class LogSystem {
private :
LogSystem () {
string logType = ConfigManager::GetConfig ("log_type" );
factory = CreateLoggerFactory (logType);
logger = factory->CreateLogger ();
cout << "日志系统初始化完成,当前日志类型:" << logType << endl;
}
LogSystem (const LogSystem&) = delete ;
LogSystem& operator =(const LogSystem&) = delete ;
LoggerFactory* CreateLoggerFactory (const string& logType) {
if (logType == "console" ) {
return new ConsoleLoggerFactory ();
} else if (logType == "file" ) {
return new FileLoggerFactory ();
} else if (logType == "db" ) {
return new DatabaseLoggerFactory ();
} else {
throw invalid_argument ("不支持的日志类型:" + logType);
}
}
static LogSystem& GetInstance () {
static LogSystem instance;
return instance;
}
LoggerFactory* factory;
Logger* logger;
public :
static void Log (const string& level, const string& message) {
GetInstance ().logger->WriteLog (level, message);
}
~LogSystem () {
delete logger;
delete factory;
}
};
void TestLog (int moduleId) {
string message = "模块" + to_string (moduleId) + "执行完成" ;
LogSystem::Log ("INFO" , message);
}
int main () {
try {
LogSystem::Log ("DEBUG" , "系统启动中..." );
LogSystem::Log ("INFO" , "系统启动成功" );
LogSystem::Log ("WARN" , "内存使用率过高(80%)" );
LogSystem::Log ("ERROR" , "数据库连接超时" );
thread modules[10 ];
for (int i = 0 ; i < 10 ; ++i) {
modules[i] = thread (TestLog, i);
}
for (int i = 0 ; i < 10 ; ++i) {
modules[i].join ();
}
cout << "日志写入完成,查看 system.log 文件" << endl;
} catch (const exception& e) {
cout << "日志系统错误:" << e.what () << endl;
}
return 0 ;
}
17.5.4 代码说明与运行效果
1. 核心设计亮点
单例 + 工厂结合:LogSystem 是单例,确保全局唯一;内部通过工厂模式创建日志器,支持扩展;
配置驱动:日志类型由 ConfigManager 配置,修改配置即可切换日志类型(如将 log_type 改为 'console' 则切换为控制台日志);
线程安全:LogSystem 和 ConfigManager 均使用 C++11 局部静态变量实现单例,支持多线程安全访问。
2. 运行结果 日志系统初始化完成,当前日志类型:file
[DEBUG] [文件] 系统启动中...
[INFO] [文件] 系统启动成功
[WARN] [文件] 内存使用率过高(80% )
[ERROR] [文件] 数据库连接超时
日志写入完成,查看 system.log 文件
[DEBUG] [文件] 系统启动中...
[INFO] [文件] 系统启动成功
[WARN] [文件] 内存使用率过高(80% )
[ERROR] [文件] 数据库连接超时
[INFO] [文件] 模块 0 执行完成
[INFO] [文件] 模块 1 执行完成
...
[INFO] [文件] 模块 9 执行完成
3. 扩展演示(新增网络日志) 若需新增'网络日志',只需添加以下代码,无需修改现有核心代码:
class NetworkLogger : public Logger {
public :
void WriteLog (const string& level, const string& message) override {
cout << "[" << level << "] [网络] 发送日志到服务器:" << message << endl;
}
};
class NetworkLoggerFactory : public LoggerFactory {
public :
Logger* CreateLogger () override { return new NetworkLogger (); }
};
configMap["log_type" ] = "network" ;
else if (logType == "network" ) {
return new NetworkLoggerFactory ();
}
17.6 本章总结 本章重点讲解了 C++ 开发中最常用的两种设计模式:工厂模式和单例模式,核心要点总结如下:
工厂模式分为三个层次,需根据产品数量和扩展性需求选择:
简单工厂:适用于产品少、变化少的场景,实现简单但扩展性差;
工厂方法:适用于产品多、需灵活扩展的场景,遵守开放 - 封闭原则;
抽象工厂:适用于管理多个相关产品族的场景,支持产品族整体切换。
单例模式的两种核心实现:
饿汉式:预加载实例,线程安全,适合实例创建成本低的场景;
懒汉式(C++11):延迟加载,线程安全,适合实例创建成本高的场景。
设计模式的核心价值是'解决特定场景下的代码问题',而非'炫技'。实际开发中,需避免过度设计,优先选择简单、易维护的方案。
实战技巧:工厂模式用于'对象创建的标准化',单例模式用于'全局唯一实例的管理',两者可结合使用(如单例的核心系统 + 工厂的组件创建),提升代码的扩展性和维护性。
通过本章学习,你应能在实际项目中灵活运用工厂模式和单例模式,解决对象创建和全局实例管理的问题,编写高内聚、低耦合的优质 C++ 代码。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online