获课地址:666it.top/14228/
在软件开发中,对象的创建是最基础也是最频繁的操作。不规范的创建逻辑会导致代码耦合度高、难以扩展和维护。创建型设计模式通过将对象的实例化过程封装起来,使系统在创建对象时更加灵活和独立。本文将深入探讨两个最常用的创建型模式:工厂方法模式和单例模式,并通过C++代码展示其威力。
一、工厂方法模式:解耦创建逻辑
1.1 模式意图
定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
1.2 UML结构图
text
[Creator] <>------> [Product]
^ ^
| |
[ConcreteCreator] -> [ConcreteProduct]
Product: 定义产品的接口。ConcreteProduct: 实现Product接口的具体类。Creator: 声明工厂方法,返回一个Product类型的对象。ConcreteCreator: 重写工厂方法,返回一个ConcreteProduct实例。
1.3 C++ 代码实现
假设我们有一个日志记录器系统,需要支持文件日志和数据库日志。
cpp
#include <iostream>
#include <memory>
// 产品接口:日志记录器
class Logger {
public:
virtual ~Logger() = default;
virtual void log(const std::string& message) = 0;
};
// 具体产品A:文件日志记录器
class FileLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "Writing Log to File: " << message << std::endl;
}
};
// 具体产品B:数据库日志记录器
class DatabaseLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "Writing Log to Database: " << message << std::endl;
}
};
// 创建者基类
class LoggerFactory {
public:
virtual ~LoggerFactory() = default;
// 工厂方法
virtual std::unique_ptr<Logger> createLogger() = 0;
// 一个使用产品的操作
void logMessage(const std::string& message) {
auto logger = createLogger();
logger->log(message);
}
};
// 具体创建者A:文件日志工厂
class FileLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<FileLogger>();
}
};
// 具体创建者B:数据库日志工厂
class DatabaseLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<DatabaseLogger>();
}
};
// 实战应用
int main() {
// 配置决定使用哪种日志(可以从配置文件中读取)
std::unique_ptr<LoggerFactory> factory;
// 模拟配置为文件日志
factory = std::make_unique<FileLoggerFactory>();
factory->logMessage("This is a system startup log.");
// 切换为数据库日志
factory = std::make_unique<DatabaseLoggerFactory>();
factory->logMessage("This is a user login log.");
return 0;
}
1.4 模式优点
- 解耦:客户端代码只需要关心
LoggerFactory接口,无需知道具体Logger类的实现。 - 可扩展:要新增一个网络日志记录器,只需创建新的
NetworkLogger和NetworkLoggerFactory,无需修改现有客户端代码。 - 符合开闭原则。
二、单例模式:确保唯一的全局访问点
2.1 模式意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2.2 关键挑战与C++实现(线程安全版)
在C++中实现一个线程安全的单例是面试和工程中的常见问题。
cpp
#include <iostream>
#include <mutex>
class ThreadSafeSingleton {
private:
// 1. 私有静态成员变量
static ThreadSafeSingleton* instance_;
static std::mutex mutex_;
// 2. 私有构造函数,防止外部实例化
ThreadSafeSingleton() {
std::cout << "ThreadSafeSingleton instance created!" << std::endl;
}
// 禁用拷贝和赋值
ThreadSafeSingleton(const ThreadSafeSingleton&) = delete;
ThreadSafeSingleton& operator=(const ThreadSafeSingleton&) = delete;
public:
// 3. 公有静态获取方法
static ThreadSafeSingleton* getInstance() {
std::lock_guard<std::mutex> lock(mutex_); // 加锁,保证线程安全
if (instance_ == nullptr) {
instance_ = new ThreadSafeSingleton();
}
return instance_;
}
// 示例业务方法
void someBusinessLogic() {
std::cout << "Executing some business logic." << std::endl;
}
// 4. 可选:释放资源的函数(注意:手动管理)
static void destroyInstance() {
std::lock_guard<std::mutex> lock(mutex_);
if (instance_ != nullptr) {
delete instance_;
instance_ = nullptr;
}
}
};
// 静态成员变量定义
ThreadSafeSingleton* ThreadSafeSingleton::instance_ = nullptr;
std::mutex ThreadSafeSingleton::mutex_;
// 实战应用:配置管理器
class ConfigurationManager {
private:
static ConfigurationManager* instance_;
static std::mutex cm_mutex_;
std::string serverAddress_;
int port_;
ConfigurationManager() : serverAddress_("127.0.0.1"), port_(8080) {} // 默认配置
public:
static ConfigurationManager* getInstance() {
std::lock_guard<std::mutex> lock(cm_mutex_);
if (instance_ == nullptr) {
instance_ = new ConfigurationManager();
}
return instance_;
}
// Getters and Setters
std::string getServerAddress() const { return serverAddress_; }
void setServerAddress(const std::string& address) { serverAddress_ = address; }
int getPort() const { return port_; }
void setPort(int port) { port_ = port; }
// 禁用拷贝和赋值
ConfigurationManager(const ConfigurationManager&) = delete;
ConfigurationManager& operator=(const ConfigurationManager&) = delete;
};
ConfigurationManager* ConfigurationManager::instance_ = nullptr;
std::mutex ConfigurationManager::cm_mutex_;
int main() {
// 使用单例配置管理器
auto config = ConfigurationManager::getInstance();
std::cout << "Server: " << config->getServerAddress() << ":" << config->getPort() << std::endl;
// 修改配置,任何地方获取的都是同一个实例
config->setServerAddress("192.168.1.100");
auto sameConfig = ConfigurationManager::getInstance();
std::cout << "Server: " << sameConfig->getServerAddress() << ":" << sameConfig->getPort() << std::endl; // 输出修改后的地址
return 0;
}
2.3 模式优缺点
- 优点:严格控制对唯一实例的访问,避免了对全局变量的滥用。
- 缺点:违反了单一职责原则(既管理业务,又控制实例化),测试困难,在分布式系统中可能不是真正的“单例”。
总结
工厂方法模式和单例模式是创建型模式中的中流砥柱。工厂方法提供了强大的扩展能力,而单例模式则在需要全局唯一实例的场景下不可或缺。理解它们的原理和实现细节,是迈向C++设计模式精通之路的第一步。在下一篇文章中,我们将探讨结构型模式,看看如何像搭积木一样组合类和对象。