前言
本篇的设计模式主要是:单例模式、工厂方法模式、策略模式、观察者模式、适配器模式。
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
特点
- 唯一实例:类自己控制实例创建,保证全局只有一个对象。
- 私有构造函数:构造函数设为
private,不适合被继承,且外部不能随意new对象。 - 禁止拷贝和赋值:删除拷贝构造、拷贝赋值、移动构造、移动赋值,防止通过复制产生第二个实例。
- 提供全局访问点:通过静态方法如
instance()或getInstance()返回唯一实例的引用或指针。 - 内部只创建一次对象:通常用
static局部变量实现。
样例及说明
单例模式因为构造函数设为private,不适合被继承,所以它的模式中很少有virtual虚函数(C++语法上是可以有,因为不适合被继承,所以很少有虚函数)。static局部变量使得它的生命周期从第一次执行到该声明语句时进行构造和初始化,而结束则是析构发生在main()函数返回之后。
static局部变量的特性在【C++开发】从裸指针到智能指针-CSDN博客中有介绍。
单例模式类实现
//configmanager.h #ifndef CONFIGMANAGER_H #define CONFIGMANAGER_H #include <QObject> #include <QSettings> /****************************************************** * 配置管理类(单例模式) * 用于统一管理应用程序的配置信息,例如用户名、窗口大小等。 * 使用 QSettings 进行本地持久化存储。 * 该类禁止拷贝和赋值,确保全局只有一个实例。 ******************************************************/ class ConfigManager : public QObject { Q_OBJECT public: //外部可调用的类成员函数 //获取 ConfigManager 全局唯一实例 //采用局部静态变量实现线程安全 static ConfigManager &instance(); QString userName() const; //返回配置文件中的用户名称 void setUserName(const QString &name); //修改配置文件中的用户名称 //保存当前配置到持久化存储 //通常在程序退出或配置变更时调用 void save(); private: //私有构造函数,防止外部通过 new 创建实例,确保单例特性 explicit ConfigManager(QObject *parent = nullptr); //禁用拷贝构造函数 ConfigManager(const ConfigManager &) = delete; //禁用赋值运算符 ConfigManager &operator=(const ConfigManager &) = delete; //用于读写配置文件 QSettings m_settings; }; #endif // CONFIGMANAGER_H// configmanager.cpp #include "configmanager.h" ConfigManager::ConfigManager(QObject *parent) : QObject(parent), m_settings("MyCompany", "SingletonDemo") { } ConfigManager &ConfigManager::instance() { static ConfigManager manager; return manager; } QString ConfigManager::userName() const { return m_settings.value("user/name", "Guest").toString(); } void ConfigManager::setUserName(const QString &name) { m_settings.setValue("user/name", name); } void ConfigManager::save() { m_settings.sync(); }调用方式
在单例模式类中明确定义了静态成员函数static 单例模式类 &instance();,只有这样它才不属于某个对象(不允许new),而是属于类本身,此时统一以此方式调用这个类的成员函数:单例模式类::instance().xxx,如下
#include "configmanager.h" void main(){ QString userName=ConfigManager::instance().userName(); QString newName="testname"; ConfigManager::instance().setUserName(newName); ConfigManager::instance().save(); }工厂方法模式
定义一个创建对象的接口,让子类决定实例化哪一个类,使创建过程延迟到子类进行。
特点
- 创建和使用分离:业务代码不直接
new具体类,而是调用工厂方法。 - 面向抽象编程:返回基类指针、引用或智能指针,上层不依赖具体实现。
- 统一创建入口:所有对象都通过工厂创建,便于集中管理初始化逻辑。
- 隐藏细节:调用者不需要知道对象内部怎么构造、依赖哪些资源。
样例及说明
基类提供纯虚函数,子类重写虚函数,并且工厂子类创建具体对象,返回具体对象的智能指针。
工厂方法类实现
产品和工厂基类
class Shape { public: virtual ~Shape() = default; virtual void draw() = 0; }; class ShapeFactory { public: virtual ~ShapeFactory() = default; virtual std::unique_ptr<Shape> create() = 0; };产品和工厂子类
创建过程延迟到子类进行。
//------------------------具体产品----------------- class Circle : public Shape { public: void draw() override { std::cout << "Draw Circle" << std::endl; } }; class Rectangle : public Shape { public: void draw() override { std::cout << "Draw Rectangle" << std::endl; } }; //------------------------具体工厂----------------- class CircleFactory : public ShapeFactory { public: std::unique_ptr<Shape> create() override { return std::make_unique<Circle>();//延迟到具体工厂创建具体对象 } }; class RectangleFactory : public ShapeFactory { public: std::unique_ptr<Shape> create() override { return std::make_unique<Rectangle>();//延迟到具体工厂创建具体对象 } };调用方法
先创建工厂之类,工厂之类在调用创建产品的函数,返回的是具体产品的智慧指针。
std::unique_ptr<ShapeFactory> factory = std::make_unique<CircleFactory>(); auto shape = factory->create(); shape->draw();策略模式
把“做什么”和“怎么做”拆开,用组合替代继承,让算法可以在运行时互换。
策略模式通常有三个角色:
- Strategy(策略接口):虚拟函数接口类,定义了所有支持算法的公共接口。
- ConcreteStrategy(具体策略):继承接口的子类,实现了具体的业务逻辑。
- Context(上下文):主框架,持有接口的指针,负责调用算法或行为。
关键点在于:Context 不关心具体是哪个策略,只认接口。
QT的低级插件QPlugin就是以策略模式构建,我之前分享的博文里面也专门介绍有:
【QT入门到晋级】低级插件QPlugin开发-CSDN博客
观察者模式
观察者模式本质是一对多的通知机制:一个对象状态变化时,自动通知所有关心它的对象,而不需要知道它们具体是谁。
QT的信号和槽机制和观察者模式契合度非常高,并且也支持一个信号发送,多个槽接收。
另外QT的事件系统也是这个机制。除了几个常用事件,需要捕获指定事件时,需要重写event()函数,根据事件类型触发相关的操作。
特点
| 角色 | 职责 |
|---|---|
| Subject(被观察者) | 维护观察者列表,提供注册/注销/通知接口 |
| Observer(观察者) | 定义统一的update()接口,接收通知 |
| ConcreteSubject | 具体被观察者,状态变化时调用notify() |
| ConcreteObserver | 具体观察者,实现update()响应逻辑 |
样例及说明
观察者基类提供纯虚函数;
观察者子类重写虚函数;
被观察者通过vector累加/减观察者,通过notify(通知)遍历vector逐个调用观察者子类重写的触发函数。
观察者模式类实现
观察者基类
class Observer { public: virtual ~Observer() = default; virtual void update(const std::string &msg) = 0; };被观察者
attach/detach/notify都可能被多线程并发调用,必须加锁。
attach(注册)、detach(注销)被直接对容器std::vector<std::shared_ptr<Observer>>进行增/删操作。
notify(通知)遍历vector逐个调用观察者子类重写的触发函数。
class Subject { public: void attach(std::shared_ptr<Observer> obs) { std::lock_guard<std::mutex> lock(mtx); observers.push_back(obs); } void detach(std::shared_ptr<Observer> obs) { std::lock_guard<std::mutex> lock(mtx); observers.erase( std::remove(observers.begin(), observers.end(), obs), observers.end()); } void notify(const std::string &msg) { std::lock_guard<std::mutex> lock(mtx); for (auto &obs : observers) { obs->update(msg); } } private: std::vector<std::shared_ptr<Observer>> observers; std::mutex mtx; };观察者子类
重写的触发函数update。
class LogObserver : public Observer { public: void update(const std::string &msg) override { std::cout << "[日志] " << msg << std::endl; } }; class UIObserver : public Observer { public: void update(const std::string &msg) override { std::cout << "[界面] 更新显示: " << msg << std::endl; } };调用方法
int main() { Subject subject; auto log = std::make_shared<LogObserver>(); auto ui = std::make_shared<UIObserver>(); subject.attach(log); subject.attach(ui); subject.notify("录像开始"); // 两个观察者都会收到 subject.notify("录像结束"); subject.detach(log); // 移除日志观察者 subject.notify("设备离线"); // 只有界面收到 }适配器模式
在不修改原有类的前提下,把一个不兼容的接口转换成客户端期望的接口。适配器模式在数据中间件、API网关、流媒体、数据接入层等场景比较常见。
特点
中间件通常要连接很多不同系统:不同协议、不同数据格式、不同厂商 SDK、不同版本 API。适配器模式正好解决这种问题:
- 把外部不兼容接口转换成内部统一接口;
- 屏蔽第三方 SDK、协议、数据格式差异;
- 新增数据源时,尽量不动核心业务代码;
- 第三方接口升级、替换时,只改适配器,不改主流程。
样例及说明
适配器模式类实现
原有接口类
class ILogger { public: virtual ~ILogger() = default; virtual void log(const std::string &msg) = 0; virtual void error(const std::string &msg) = 0; };第三接口类
class FastLogger { public: void writeLog(const char *msg, int level); };接口适配器类
在构造函数中转移右值,重写虚函数
// 把 FastLogger 适配成 ILogger class LoggerAdapter : public ILogger {//继承原有接口 public: explicit LoggerAdapter(std::unique_ptr<FastLogger> logger)//转移右值 : logger_(std::move(logger)) {} void log(const std::string &msg) override {//重写虚函数 logger_->writeLog(msg.c_str(), 0); } void error(const std::string &msg) override {//重写虚函数 logger_->writeLog(msg.c_str(), 1); } private: std::unique_ptr<FastLogger> logger_; };调用方法
// 业务代码:只认 ILogger,完全不知道 FastLogger 的存在 class OrderService { public: explicit OrderService(ILogger &logger) : logger_(logger) {} void createOrder() { logger_.log("creating order..."); logger_.error("create order failed: insufficient stock"); } private: ILogger &logger_; }; int main() { // 把第三方对象塞进适配器 auto logger = std::make_unique<LoggerAdapter>(std::make_unique<FastLogger>()); //方法一:自己直接调用 logger->log("app started");//直接调 logger->error("something went wrong");//直接调 //方法二:把适配器"借"给业务类 OrderService service(*logger); service.createOrder(); return 0; }