51CTO 【夏CJ13912】C++ 设计模式理论与实战大全-架构师必学视频课程

39 阅读4分钟

获课地址: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类的实现。
  • 可扩展:要新增一个网络日志记录器,只需创建新的NetworkLoggerNetworkLoggerFactory,无需修改现有客户端代码。
  • 符合开闭原则

二、单例模式:确保唯一的全局访问点

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++设计模式精通之路的第一步。在下一篇文章中,我们将探讨结构型模式,看看如何像搭积木一样组合类和对象。