Qt 设计模式:工厂模式
系列:Qt / VTK 设计模式 · Qt 06/11
参考:Qt 6 QStyleFactory、Plugins
引子
只需要传入字符串 "Fusion" 或 "Windows",调用 QStyleFactory::create("Fusion") 一行代码就能拿到对应平台的皮肤样式对象——业务调用方完全不需要知道 QWindowsStyle 或 QFusionStyle 具体类的存在与创建细节。
这就是工厂模式(Factory Pattern):按条件动态创建对象,向调用方隐藏具体的类类型与初始化过程。在 Qt 中,工厂模式结合了元对象系统(MOC)与动态插件机制,构成了扩展性极强的创建型基础设施。
核心痛点与 GoF 工厂分类
痛点场景:直接 new 具体类的缺陷
cpp
// 坏方案:业务代码中充斥具体的类依赖与条件分支
QStyle* style = nullptr;
if (platform == "windows") {
style = new QWindowsStyle(); // 强绑定物理类头文件
} else if (platform == "fusion") {
style = new QFusionStyle();
}- 编译耦合强:业务代码必须包含所有具体子类的头文件。
- 扩展性差:新增一种样式,所有包含条件分支的业务代码都需要修改并重新编译。
GoF 三种工厂模式在 Qt 中的映射
| 工厂模式分类 | Qt 代表实现 | 机制与职责说明 |
|---|---|---|
| 简单工厂 (Simple Factory) | QStyleFactory::create(key) | 根据字符串 Key 静态查找并创建具体的 QStyle 实例 |
| 工厂方法 (Factory Method) | QPluginLoader::instance() | 定义工厂接口,由具体的 DLL/SO 插件重写工厂方法创建具体对象 |
| 抽象工厂 (Abstract Factory) | QPlatformIntegration (Qt 底层 QPA) | 创建一整套相关的底层平台组件(如窗口、画刷、事件循环) |
底层原理:Qt 插件工厂与元数据注册
QStyleFactory 和 Qt 插件系统(QPA)的核心是将“工厂创建表”解耦到运行期:
- 每个插件通过
Q_PLUGIN_METADATA导出 JSON 元数据。 - 工厂通过
QPluginLoader动态加载.dll/.so库。 - 通过
qobject_cast转换为抽象接口指针返回给调用方,彻底解除编译期头文件依赖。
完整代码示例
1. 简单工厂与 QStyleFactory 的使用
cpp
#include <QApplication>
#include <QStyleFactory>
#include <QPushButton>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 查看当前系统支持的所有样式工厂 Key
QStringList keys = QStyleFactory::keys();
qDebug() << "Available Styles:" << keys;
// 使用简单工厂按名称动态创建产品,接管渲染样式
if (keys.contains("Fusion", Qt::CaseInsensitive)) {
QStyle* fusionStyle = QStyleFactory::create("Fusion");
QApplication::setStyle(fusionStyle); // QApplication 接管指针所有权
}
QPushButton btn("Hello Qt Factory");
btn.show();
return app.exec();
}2. 工厂方法:基于 Qt 插件架构的抽象工厂实现
接口定义 (IInterface.h):
cpp
#pragma once
#include <QtPlugin>
class ISensorDevice {
public:
virtual ~ISensorDevice() = default;
virtual void readData() = 0;
};
// 注册接口 ID
#define ISensorDevice_IID "com.example.ISensorDevice/1.0"
Q_DECLARE_INTERFACE(ISensorDevice, ISensorDevice_IID)插件工厂实现 (MyPlugin.cpp):
cpp
#include "IInterface.h"
#include <QObject>
class OpticalSensorPlugin : public QObject, public ISensorDevice {
Q_OBJECT
Q_PLUGIN_METADATA(IID ISensorDevice_IID)
Q_INTERFACES(ISensorDevice)
public:
void readData() override {
// 具体的传感器数据采集实现
}
};调用方运行期加载 (Client.cpp):
cpp
#include "IInterface.h"
#include <QPluginLoader>
#include <QDebug>
void loadAndUseSensor(const QString& pluginPath) {
QPluginLoader loader(pluginPath);
QObject* pluginInstance = loader.instance();
if (pluginInstance) {
// 工厂转换:通过 qobject_cast 拿到接口
auto* sensor = qobject_cast<ISensorDevice*>(pluginInstance);
if (sensor) {
sensor->readData();
}
} else {
qWarning() << "Plugin Load Failed:" << loader.errorString();
}
}易混淆模式对比
| 设计模式 | 核心目的 | 区别说明 |
|---|---|---|
| 工厂模式 (Factory) | 按条件创建单个对象 | 一次性根据参数生成并返回一个产品的抽象指针 |
| 单例模式 (Singleton) | 保证全局唯一实例 | 全局只有一个共享对象(工厂可频繁创建不同新对象) |
| 建造者模式 (Builder) | 分步组装复杂对象 | 工厂侧重于“选哪种产品”,建造者侧重于“零部件的组合过程” |
最佳实践与踩坑避坑
- 资源与所有权接管:
QStyleFactory::create()返回的QStyle*在被QApplication::setStyle()接管后由 Qt 负责销毁;如果未传入QApplication,调用方必须自行内存释放(推荐使用std::unique_ptr)。
- 插件加载失败调试:
- 动态加载 DLL/SO 插件时,必须检查
loader.errorString()。常见失败原因为依赖的 Qt 动态库版本不匹配或缺少依赖库。
- 动态加载 DLL/SO 插件时,必须检查
- 结合反射解耦解组:
- Qt 的
QUiLoader/QFormBuilder通过读取.uiXML 文件中的类名字符串(如"QPushButton"),反射实例化对应的控件,是工厂模式在界面解耦上的极致落地。
- Qt 的
重点与注意
重点:
QStyleFactory::create(key)是简单工厂模式在 Qt 中的代表,调用方完全解耦了具体的样式类头文件。
重点:Qt 插件机制(QPluginLoader+Q_PLUGIN_METADATA)是工厂方法模式的工业级实现,支持跨 DLL 扩展。
注意:工厂模式解决的是“对象怎么创建”的问题;创建出来的对象生命周期管理依然需要严格遵循 Qt 对象树或 Smart Pointer 规则。
小结
Qt 工厂模式隐藏了复杂对象的初始化细节,实现了物理类型与业务调用的彻底分离。
延伸阅读