欢迎光临
我们一直在努力

设计模式在C++中的实战应用(一):创建型模式

第21章 设计模式在C++中的实战应用(一):创建型模式

在这里插入图片描述

21.1 学习目标与重点

  • 掌握创建型模式的核心思想:封装对象创建过程,解耦对象创建与使用逻辑,提升代码灵活性和可维护性🗄️。
  • 深入理解单例模式、工厂方法模式、抽象工厂模式的设计原理、适用场景及C++实现细节。
  • 能够根据实际开发需求,合理选择创建型模式解决对象创建相关的工程问题。
  • 重点:单例模式的线程安全实现、工厂模式的层级扩展、抽象工厂模式的产品族设计。

21.2 创建型模式概述

💡 设计模式是软件开发中反复出现的问题的成熟解决方案,而创建型模式是设计模式的三大分类之一(另外两类为结构型模式、行为型模式),其核心关注点是如何创建对象。

在C++开发中,直接使用new关键字创建对象看似简单,但在复杂场景下会存在诸多问题:

  • 对象创建逻辑复杂(如需要初始化多个参数、依赖其他对象、选择不同子类实现)时,会导致创建代码冗余且分散;
  • 客户端与具体类强耦合,更换对象类型时需要修改大量客户端代码;
  • 无法灵活控制对象的创建时机、生命周期(如全局唯一对象、池化对象)。
  • 创建型模式通过封装对象创建细节,将创建逻辑与使用逻辑分离,让客户端无需关注“如何创建对象”,只需专注“如何使用对象”,从而提升代码的可扩展性、可维护性和复用性。

    本章重点讲解3种最常用的创建型模式:单例模式、工厂方法模式、抽象工厂模式,后续章节将继续介绍建造者模式和原型模式。

    21.3 单例模式(Singleton Pattern)

    21.3.1 核心思想与适用场景

    💡 单例模式的核心是:确保一个类在整个程序运行期间只有一个实例对象,并提供一个全局访问点。

    适用场景:
    • 系统中需要一个全局管理类(如配置管理器、日志管理器、数据库连接池);
    • 对象创建成本高(如占用大量内存、依赖复杂资源),无需多个实例;
    • 需严格控制对象实例数量,避免资源竞争或状态不一致。
    设计要点:
  • 私有构造函数(禁止外部通过new创建对象);
  • 私有静态成员变量(存储唯一实例);
  • 公有静态成员方法(提供全局访问点,负责创建/返回实例)。
  • 21.3.2 C++实现方式(从基础到线程安全)

    1. 基础懒汉式(非线程安全)

    懒汉式:延迟初始化,即第一次调用时才创建实例。

    #include <iostream>
    // 日志管理器(单例模式-基础懒汉式)
    class LogManager {
    private:
    // 1. 私有构造函数:禁止外部创建对象
    LogManager() {
    std::cout << "LogManager实例创建成功" << std::endl;
    }
    // 2. 私有静态成员变量:存储唯一实例(指针形式,延迟初始化)
    static LogManager* instance_;

    public:
    // 3. 公有静态方法:全局访问点
    static LogManager* GetInstance() {
    // 第一次调用时创建实例
    if (instance_ == nullptr) {
    instance_ = new LogManager();
    }
    return instance_;
    }

    // 成员方法:日志输出
    void Log(const std::string& message) {
    std::cout << "[Log] " << message << std::endl;
    }

    // 禁止拷贝构造和赋值运算符(避免创建多个实例)
    LogManager(const LogManager&) = delete;
    LogManager& operator=(const LogManager&) = delete;
    };

    // 静态成员变量初始化(类外)
    LogManager* LogManager::instance_ = nullptr;

    // 客户端使用
    int main() {
    // 无法通过new创建对象(编译报错)
    // LogManager* log = new LogManager();

    // 通过全局访问点获取实例
    LogManager* log1 = LogManager::GetInstance();
    LogManager* log2 = LogManager::GetInstance();

    // 验证是否为同一个实例(地址相同)
    std::cout << "log1地址:" << log1 << std::endl;
    std::cout << "log2地址:" << log2 << std::endl;

    // 使用实例
    log1->Log("程序启动成功");
    log2->Log("用户登录");

    return 0;
    }

    运行结果:

    LogManager实例创建成功
    log1地址:0x7f8a9b000b20
    log2地址:0x7f8a9b000b20
    [Log] 程序启动成功
    [Log] 用户登录

    ⚠️ 注意:基础懒汉式在多线程环境下存在线程安全问题。当多个线程同时调用GetInstance()时,可能会执行多次new操作,创建多个实例(违反单例原则)。

    2. 线程安全的懒汉式(加锁实现)

    通过互斥锁(std::mutex)保护实例创建过程,确保同一时间只有一个线程能执行创建逻辑。

    #include <iostream>
    #include <mutex> // 互斥锁头文件
    #include <thread> // 线程头文件

    class LogManager {
    private:
    LogManager() {
    std::cout << "LogManager实例创建成功" << std::endl;
    }
    static LogManager* instance_;
    static std::mutex mutex_; // 静态互斥锁(保护实例创建)

    public:
    static LogManager* GetInstance() {
    // 双重检查锁定(DCLP):提升性能,避免每次调用都加锁
    if (instance_ == nullptr) { // 第一次检查:无锁,快速判断
    std::lock_guard<std::mutex> lock(mutex_); // 加锁(自动释放)
    if (instance_ == nullptr) { // 第二次检查:加锁后再次确认
    instance_ = new LogManager();
    }
    }
    return instance_;
    }

    void Log(const std::string& message) {
    std::cout << "[Log] " << message << std::endl;
    }

    // 禁止拷贝和赋值
    LogManager(const LogManager&) = delete;
    LogManager& operator=(const LogManager&) = delete;
    };

    // 静态成员初始化
    LogManager* LogManager::instance_ = nullptr;
    std::mutex LogManager::mutex_;

    // 线程函数:模拟多线程调用
    void ThreadFunc(int thread_id) {
    LogManager* log = LogManager::GetInstance();
    log->Log("线程" + std::to_string(thread_id) + "调用成功");
    }

    int main() {
    // 创建5个线程,同时调用单例
    std::thread t1(ThreadFunc, 1);
    std::thread t2(ThreadFunc, 2);
    std::thread t3(ThreadFunc, 3);
    std::thread t4(ThreadFunc, 4);
    std::thread t5(ThreadFunc, 5);

    // 等待线程结束
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();

    return 0;
    }

    运行结果:

    LogManager实例创建成功
    [Log] 线程1调用成功
    [Log] 线程3调用成功
    [Log] 线程2调用成功
    [Log] 线程5调用成功
    [Log] 线程4调用成功

    💡 技巧:双重检查锁定(DCLP)是线程安全懒汉式的优化方案。第一次无锁检查避免了每次调用都加锁的性能开销,第二次加锁检查确保了线程安全,是兼顾性能和安全性的常用实现。

    3. 饿汉式(线程安全,推荐简单场景)

    饿汉式:程序启动时(静态变量初始化阶段)就创建实例,无需加锁,天然线程安全。

    #include <iostream>
    #include <thread>

    class ConfigManager {
    private:
    // 私有构造函数:初始化配置(模拟从文件加载)
    ConfigManager() {
    std::cout << "ConfigManager实例创建,加载配置文件…" << std::endl;
    // 模拟配置初始化
    config_map_["timeout"] = "30s";
    config_map_["max_conn"] = "1000";
    }

    // 私有静态成员变量:程序启动时初始化(饿汉式核心)
    static ConfigManager instance_;

    // 配置数据
    std::unordered_map<std::string, std::string> config_map_;

    public:
    // 全局访问点:返回静态实例的引用(避免指针拷贝)
    static ConfigManager& GetInstance() {
    return instance_;
    }

    // 获取配置值
    std::string GetConfig(const std::string& key) {
    auto it = config_map_.find(key);
    return it != config_map_.end() ? it->second : "default";
    }

    // 禁止拷贝和赋值
    ConfigManager(const ConfigManager&) = delete;
    ConfigManager& operator=(const ConfigManager&) = delete;
    };

    // 静态实例初始化(类外,程序启动时执行)
    ConfigManager ConfigManager::instance_;

    // 线程函数:模拟多线程读取配置
    void ReadConfig(int thread_id) {
    ConfigManager& config = ConfigManager::GetInstance();
    std::cout << "线程" << thread_id << "获取timeout配置:" << config.GetConfig("timeout") << std::endl;
    }

    int main() {
    std::cout << "程序启动,开始创建线程…" << std::endl;
    std::thread t1(ReadConfig, 1);
    std::thread t2(ReadConfig, 2);

    t1.join();
    t2.join();

    return 0;
    }

    运行结果:

    ConfigManager实例创建,加载配置文件…
    程序启动,开始创建线程…
    线程1获取timeout配置:30s
    线程2获取timeout配置:30s

    单例模式实现对比
    实现方式线程安全初始化时机优点缺点
    基础懒汉式 ❌ 否 第一次调用时 延迟初始化,节省内存 多线程下不安全
    加锁懒汉式(DCLP) ✅ 是 第一次调用时 延迟初始化+线程安全 实现稍复杂,有微小锁开销
    饿汉式 ✅ 是 程序启动时 实现简单,无锁开销 提前占用内存,不支持延迟

    ✅ 结论:

    • 简单场景(无多线程或对象占用内存小):优先使用饿汉式,实现简单且安全;
    • 复杂场景(多线程+对象创建成本高):使用加锁懒汉式(DCLP),兼顾延迟初始化和线程安全。

    21.3.3 实战案例:C++日志管理器(单例模式应用)

    以下是一个完整的日志管理器实现,结合单例模式+文件输出,支持日志级别、时间戳记录:

    #include <iostream>
    #include <fstream>
    #include <mutex>
    #include <ctime>
    #include <string>

    // 日志级别枚举
    enum class LogLevel {
    DEBUG,
    INFO,
    WARN,
    ERROR
    };

    class LogManager {
    private:
    LogManager() {
    // 初始化日志文件(以当前时间命名)
    std::time_t now = std::time(nullptr);
    std::string filename = "log_" + std::to_string(now) + ".txt";
    log_file_.open(filename, std::ios::out | std::ios::app);
    if (!log_file_.is_open()) {
    std::cerr << "日志文件打开失败!" << std::endl;
    }
    }

    static LogManager* instance_;
    static std::mutex mutex_;
    std::ofstream log_file_; // 日志文件流

    // 辅助函数:将日志级别转为字符串
    std::string LevelToString(LogLevel level) {
    switch (level) {
    case LogLevel::DEBUG: return "DEBUG";
    case LogLevel::INFO: return "INFO";
    case LogLevel::WARN: return "WARN";
    case LogLevel::ERROR: return "ERROR";
    default: return "UNKNOWN";
    }
    }

    // 辅助函数:获取当前时间戳
    std::string GetTimestamp() {
    std::time_t now = std::time(nullptr);
    char buf[64];
    std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now));
    return buf;
    }

    public:
    static LogManager* GetInstance() {
    if (instance_ == nullptr) {
    std::lock_guard<std::mutex> lock(mutex_);
    if (instance_ == nullptr) {
    instance_ = new LogManager();
    }
    }
    return instance_;
    }

    // 日志输出接口(支持控制台+文件双输出)
    void Log(LogLevel level, const std::string& message) {
    std::string timestamp = GetTimestamp();
    std::string level_str = LevelToString(level);
    std::string log_msg = "[" + timestamp + "] [" + level_str + "] " + message + "\\n";

    // 控制台输出
    std::cout << log_msg;
    // 文件输出(加锁,避免多线程写文件冲突)
    std::lock_guard<std::mutex> lock(mutex_);
    if (log_file_.is_open()) {
    log_file_ << log_msg;
    log_file_.flush(); // 立即写入文件
    }
    }

    // 简化接口(重载)
    void Debug(const std::string& message) { Log(LogLevel::DEBUG, message); }
    void Info(const std::string& message) { Log(LogLevel::INFO, message); }
    void Warn(const std::string& message) { Log(LogLevel::WARN, message); }
    void Error(const std::string& message) { Log(LogLevel::ERROR, message); }

    // 析构函数:关闭文件
    ~LogManager() {
    if (log_file_.is_open()) {
    log_file_.close();
    }
    }

    // 禁止拷贝和赋值
    LogManager(const LogManager&) = delete;
    LogManager& operator=(const LogManager&) = delete;
    };

    // 静态成员初始化
    LogManager* LogManager::instance_ = nullptr;
    std::mutex LogManager::mutex_;

    // 客户端使用示例
    int main() {
    LogManager* logger = LogManager::GetInstance();

    logger->Debug("初始化数据库连接池");
    logger->Info("程序启动成功,版本v1.0.0");
    logger->Warn("内存使用率超过80%");
    logger->Error("数据库连接失败:无法连接到127.0.0.1:3306");

    return 0;
    }

    运行结果(控制台):

    [2024-05-20 15:30:45] [DEBUG] 初始化数据库连接池
    [2024-05-20 15:30:45] [INFO] 程序启动成功,版本v1.0.0
    [2024-05-20 15:30:45] [WARN] 内存使用率超过80%
    [2024-05-20 15:30:45] [ERROR] 数据库连接失败:无法连接到127.0.0.1:3306

    日志文件(log_1716207045.txt) 中会同步记录上述内容,实现了全局唯一的日志管理功能。

    21.4 工厂方法模式(Factory Method Pattern)

    21.4.1 核心思想与适用场景

    💡 工厂方法模式的核心是:定义一个创建对象的接口(工厂接口),但由子类决定要创建哪个类的对象。即“工厂父类负责定义创建对象的公共接口,工厂子类负责生成具体对象”。

    解决的问题:

    直接new具体类导致客户端与具体产品强耦合。例如:

    // 直接耦合:客户端需要知道具体类名,更换产品时需修改代码
    Shape* shape = new Circle();
    // 若需更换为Square,需修改为:Shape* shape = new Square();

    适用场景:
    • 客户端不需要知道具体产品的类名,只需知道对应的工厂;
    • 系统中有多个产品家族,且产品之间存在共性(可抽象为基类);
    • 需动态扩展产品类型(新增产品时,只需新增对应的工厂子类,无需修改原有代码)。
    角色划分:
  • 抽象产品(Product):所有具体产品的基类(纯虚类);
  • 具体产品(ConcreteProduct):抽象产品的实现类;
  • 抽象工厂(Factory):定义创建产品的接口(纯虚方法);
  • 具体工厂(ConcreteFactory):实现抽象工厂接口,创建具体产品。
  • 21.4.2 C++实现示例:图形绘制工厂

    假设我们需要开发一个图形绘制系统,支持圆形、矩形、三角形绘制,且未来可能扩展更多图形。使用工厂方法模式解耦客户端与具体图形类:

    1. 定义抽象产品与具体产品

    #include <iostream>
    #include <string>

    // 抽象产品:图形(Shape)
    class Shape {
    public:
    virtual ~Shape() {} // 虚析构函数:确保子类析构正常调用
    // 纯虚方法:绘制图形(子类必须实现)
    virtual void Draw() const = 0;
    virtual std::string GetName() const = 0;
    };

    // 具体产品1:圆形(Circle)
    class Circle : public Shape {
    public:
    void Draw() const override {
    std::cout << "绘制圆形:○" << std::endl;
    }
    std::string GetName() const override {
    return "Circle";
    }
    };

    // 具体产品2:矩形(Rectangle)
    class Rectangle : public Shape {
    public:
    void Draw() const override {
    std::cout << "绘制矩形:□" << std::endl;
    }
    std::string GetName() const override {
    return "Rectangle";
    }
    };

    // 具体产品3:三角形(Triangle)
    class Triangle : public Shape {
    public:
    void Draw() const override {
    std::cout << "绘制三角形:△" << std::endl;
    }
    std::string GetName() const override {
    return "Triangle";
    }
    };

    2. 定义抽象工厂与具体工厂

    // 抽象工厂:图形工厂(ShapeFactory)
    class ShapeFactory {
    public:
    virtual ~ShapeFactory() {}
    // 纯虚方法:创建图形(工厂方法)
    virtual Shape* CreateShape() const = 0;
    };

    // 具体工厂1:圆形工厂(CircleFactory)
    class CircleFactory : public ShapeFactory {
    public:
    Shape* CreateShape() const override {
    return new Circle(); // 创建圆形产品
    }
    };

    // 具体工厂2:矩形工厂(RectangleFactory)
    class RectangleFactory : public ShapeFactory {
    public:
    Shape* CreateShape() const override {
    return new Rectangle(); // 创建矩形产品
    }
    };

    // 具体工厂3:三角形工厂(TriangleFactory)
    class TriangleFactory : public ShapeFactory {
    public:
    Shape* CreateShape() const override {
    return new Triangle(); // 创建三角形产品
    }
    };

    3. 客户端使用

    // 客户端函数:使用工厂创建并绘制图形
    void DrawShape(const ShapeFactory& factory) {
    // 客户端无需知道具体产品类,只需通过工厂获取产品
    Shape* shape = factory.CreateShape();
    std::cout << "正在绘制图形:" << shape->GetName() << std::endl;
    shape->Draw();
    delete shape; // 释放资源
    }

    int main() {
    // 1. 使用圆形工厂
    CircleFactory circle_factory;
    DrawShape(circle_factory);

    // 2. 使用矩形工厂
    RectangleFactory rect_factory;
    DrawShape(rect_factory);

    // 3. 使用三角形工厂
    TriangleFactory triangle_factory;
    DrawShape(triangle_factory);

    return 0;
    }

    运行结果:

    正在绘制图形:Circle
    绘制圆形:○
    正在绘制图形:Rectangle
    绘制矩形:□
    正在绘制图形:Triangle
    绘制三角形:△

    21.4.3 扩展产品:新增图形(符合开闭原则)

    假设需要新增“菱形(Diamond)”图形,只需新增具体产品和具体工厂,无需修改原有代码(符合开闭原则:对扩展开放,对修改关闭):

    // 新增具体产品:菱形(Diamond)
    class Diamond : public Shape {
    public:
    void Draw() const override {
    std::cout << "绘制菱形:◇" << std::endl;
    }
    std::string GetName() const override {
    return "Diamond";
    }
    };

    // 新增具体工厂:菱形工厂(DiamondFactory)
    class DiamondFactory : public ShapeFactory {
    public:
    Shape* CreateShape() const override {
    return new Diamond();
    }
    };

    // 客户端使用新增产品(无需修改原有客户端代码)
    int main() {
    // … 原有代码 …

    // 新增:使用菱形工厂
    DiamondFactory diamond_factory;
    DrawShape(diamond_factory);

    return 0;
    }

    新增运行结果:

    正在绘制图形:Diamond
    绘制菱形:◇

    ⚠️ 注意:工厂方法模式中,一个具体工厂对应一个具体产品。如果需要创建一组相关联的产品(如“Windows风格控件”包含按钮、文本框、下拉框),工厂方法模式会产生大量工厂类,此时应使用抽象工厂模式。

    21.5 抽象工厂模式(Abstract Factory Pattern)

    21.5.1 核心思想与适用场景

    💡 抽象工厂模式的核心是:提供一个接口,用于创建一组相关或相互依赖的对象,而无需指定它们的具体类。

    与工厂方法模式的区别:

    • 工厂方法模式:关注“单个产品”的创建,一个工厂对应一个产品;
    • 抽象工厂模式:关注“产品族”的创建,一个工厂对应一组相关产品。
    产品族与产品等级结构:
    • 产品族:同一工厂生产的、功能相关的一组产品(如“Windows风格控件”包含按钮、文本框、下拉框);
    • 产品等级结构:同一类产品的不同实现(如“按钮”包含Windows按钮、Mac按钮、Linux按钮)。
    适用场景:
    • 系统需要使用多个产品族,且产品族内的产品相互依赖或配合使用;
    • 系统需要切换产品族(如从Windows风格切换到Mac风格),且切换时无需修改客户端代码;
    • 客户端不关心产品的创建细节,只关心产品族的选择。
    角色划分:
  • 抽象产品(AbstractProduct):产品族中每个产品的基类(如按钮基类、文本框基类);
  • 具体产品(ConcreteProduct):抽象产品的具体实现(如Windows按钮、Mac文本框);
  • 抽象工厂(AbstractFactory):定义创建产品族中所有产品的接口(如创建按钮、创建文本框的方法);
  • 具体工厂(ConcreteFactory):实现抽象工厂接口,创建某一产品族的所有产品(如Windows控件工厂创建Windows按钮、Windows文本框)。
  • 21.5.2 C++实现示例:跨平台控件库

    假设我们需要开发一个跨平台控件库,支持Windows和Mac两个平台,每个平台包含按钮(Button)、文本框(TextBox)、下拉框(ComboBox)三个相关控件(产品族)。使用抽象工厂模式实现平台切换:

    1. 定义抽象产品(产品族中的各个产品基类)

    #include <iostream>
    #include <string>

    // 抽象产品1:按钮(Button)
    class Button {
    public:
    virtual ~Button() {}
    virtual void Render() const = 0; // 渲染按钮
    };

    // 抽象产品2:文本框(TextBox)
    class TextBox {
    public:
    virtual ~TextBox() {}
    virtual void Render() const = 0; // 渲染文本框
    };

    // 抽象产品3:下拉框(ComboBox)
    class ComboBox {
    public:
    virtual ~ComboBox() {}
    virtual void Render() const = 0; // 渲染下拉框
    };

    2. 定义具体产品(各平台的产品实现)

    // 具体产品1:Windows按钮
    class WindowsButton : public Button {
    public:
    void Render() const override {
    std::cout << "渲染Windows风格按钮:[确定]" << std::endl;
    }
    };

    // 具体产品2:Windows文本框
    class WindowsTextBox : public TextBox {
    public:
    void Render() const override {
    std::cout << "渲染Windows风格文本框:[________________]" << std::endl;
    }
    };

    // 具体产品3:Windows下拉框
    class WindowsComboBox : public ComboBox {
    public:
    void Render() const override {
    std::cout << "渲染Windows风格下拉框:[选项1 ▼]" << std::endl;
    }
    };

    // 具体产品4:Mac按钮
    class MacButton : public Button {
    public:
    void Render() const override {
    std::cout << "渲染Mac风格按钮:● 确定 ●" << std::endl;
    }
    };

    // 具体产品5:Mac文本框
    class MacTextBox : public TextBox {
    public:
    void Render() const override {
    std::cout << "渲染Mac风格文本框:[⎡⎤________________]" << std::endl;
    }
    };

    // 具体产品6:Mac下拉框
    class MacComboBox : public ComboBox {
    public:
    void Render() const override {
    std::cout << "渲染Mac风格下拉框:[选项1 ▾]" << std::endl;
    }
    };

    3. 定义抽象工厂与具体工厂(产品族工厂)

    // 抽象工厂:控件工厂(创建产品族的所有产品)
    class WidgetFactory {
    public:
    virtual ~WidgetFactory() {}
    virtual Button* CreateButton() const = 0; // 创建按钮
    virtual TextBox* CreateTextBox() const = 0; // 创建文本框
    virtual ComboBox* CreateComboBox() const = 0; // 创建下拉框
    };

    // 具体工厂1:Windows控件工厂(创建Windows产品族)
    class WindowsWidgetFactory : public WidgetFactory {
    public:
    Button* CreateButton() const override {
    return new WindowsButton();
    }
    TextBox* CreateTextBox() const override {
    return new WindowsTextBox();
    }
    ComboBox* CreateComboBox() const override {
    return new WindowsComboBox();
    }
    };

    // 具体工厂2:Mac控件工厂(创建Mac产品族)
    class MacWidgetFactory : public WidgetFactory {
    public:
    Button* CreateButton() const override {
    return new MacButton();
    }
    TextBox* CreateTextBox() const override {
    return new MacTextBox();
    }
    ComboBox* CreateComboBox() const override {
    return new MacComboBox();
    }
    };

    4. 客户端使用(切换产品族)

    // 客户端函数:渲染界面(依赖抽象工厂,不关心具体平台)
    void RenderUI(const WidgetFactory& factory) {
    std::cout << "开始渲染界面…" << std::endl;

    // 创建产品族的所有产品
    Button* button = factory.CreateButton();
    TextBox* textbox = factory.CreateTextBox();
    ComboBox* combobox = factory.CreateComboBox();

    // 使用产品
    button->Render();
    textbox->Render();
    combobox->Render();

    // 释放资源
    delete button;
    delete textbox;
    delete combobox;

    std::cout << "界面渲染完成!" << std::endl << std::endl;
    }

    int main() {
    // 1. 渲染Windows风格界面
    WindowsWidgetFactory windows_factory;
    RenderUI(windows_factory);

    // 2. 渲染Mac风格界面(只需更换工厂,客户端代码无需修改)
    MacWidgetFactory mac_factory;
    RenderUI(mac_factory);

    return 0;
    }

    运行结果:

    开始渲染界面…
    渲染Windows风格按钮:[确定]
    渲染Windows风格文本框:[________________]
    渲染Windows风格下拉框:[选项1 ▼]
    界面渲染完成!

    开始渲染界面…
    渲染Mac风格按钮:● 确定 ●
    渲染Mac风格文本框:[⎡⎤________________]
    渲染Mac风格下拉框:[选项1 ▾]
    界面渲染完成!

    21.5.3 扩展产品族:新增Linux平台(符合开闭原则)

    若需新增Linux平台控件(产品族),只需新增具体产品和具体工厂,无需修改客户端和原有工厂代码:

    // 新增具体产品:Linux控件
    class LinuxButton : public Button {
    public:
    void Render() const override {
    std::cout << "渲染Linux风格按钮:<确定>" << std::endl;
    }
    };

    class LinuxTextBox : public TextBox {
    public:
    void Render() const override {
    std::cout << "渲染Linux风格文本框:[▭▭▭▭▭▭▭▭▭▭]" << std::endl;
    }
    };

    class LinuxComboBox : public ComboBox {
    public:
    void Render() const override {
    std::cout << "渲染Linux风格下拉框:[选项1 ↓]" << std::endl;
    }
    };

    // 新增具体工厂:Linux控件工厂
    class LinuxWidgetFactory : public WidgetFactory {
    public:
    Button* CreateButton() const override {
    return new LinuxButton();
    }
    TextBox* CreateTextBox() const override {
    return new LinuxTextBox();
    }
    ComboBox* CreateComboBox() const override {
    return new LinuxComboBox();
    }
    };

    // 客户端使用Linux界面
    int main() {
    // … 原有代码 …

    // 新增:渲染Linux风格界面
    LinuxWidgetFactory linux_factory;
    RenderUI(linux_factory);

    return 0;
    }

    新增运行结果:

    开始渲染界面…
    渲染Linux风格按钮:<确定>
    渲染Linux风格文本框:[▭▭▭▭▭▭▭▭▭▭]
    渲染Linux风格下拉框:[选项1 ↓]
    界面渲染完成!

    ⚠️ 注意:抽象工厂模式的缺点是扩展产品等级结构困难。例如,若需在所有平台中新增“复选框(CheckBox)”产品(新增产品等级),则需要修改抽象工厂接口及所有具体工厂类,违反开闭原则。因此,抽象工厂模式适用于产品族相对稳定的场景。

    21.6 三种创建型模式的对比与选择

    模式核心关注点适用场景优点缺点
    单例模式 单个对象的全局唯一性 全局管理类(日志、配置、连接池) 节省资源,全局统一访问 线程安全实现复杂,扩展性差
    工厂方法模式 单个产品的创建解耦 单个产品的动态扩展(如新增图形、算法) 符合开闭原则,扩展灵活 产品族扩展时工厂类冗余
    抽象工厂模式 产品族的创建解耦 多平台、多风格的产品族切换(如控件库、框架) 产品族切换方便,一致性强 扩展产品等级结构困难

    ✅ 选择建议:

  • 需全局唯一对象 → 单例模式;
  • 需独立扩展单个产品 → 工厂方法模式;
  • 需一组相关产品配合使用且需切换产品族 → 抽象工厂模式。
  • 21.7 实战练习

  • 设计一个线程安全的数据库连接池(单例模式),支持连接的创建、获取、释放,限制最大连接数为10。
  • 使用工厂方法模式设计一个文件解析器系统,支持TXT、JSON、XML三种文件格式的解析,要求新增文件格式时无需修改原有代码。
  • 使用抽象工厂模式设计一个智能家居控制系统,支持“小米”和“华为”两个产品族,每个产品族包含灯光、空调、窗帘三种设备,要求能够切换产品族并控制设备的开关操作。
  • 赞(0)
    未经允许不得转载:171主机测评 » 设计模式在C++中的实战应用(一):创建型模式
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址