Skip to content

Qt 设计模式:工厂模式

系列:Qt / VTK 设计模式 · Qt 06/11
参考:Qt 6 QStyleFactoryPlugins


引子

只需要传入字符串 "Fusion""Windows",调用 QStyleFactory::create("Fusion") 一行代码就能拿到对应平台的皮肤样式对象——业务调用方完全不需要知道 QWindowsStyleQFusionStyle 具体类的存在与创建细节。

这就是工厂模式(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)的核心是将“工厂创建表”解耦到运行期:

  1. 每个插件通过 Q_PLUGIN_METADATA 导出 JSON 元数据。
  2. 工厂通过 QPluginLoader 动态加载 .dll / .so 库。
  3. 通过 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)分步组装复杂对象工厂侧重于“选哪种产品”,建造者侧重于“零部件的组合过程”

最佳实践与踩坑避坑

  1. 资源与所有权接管
    • QStyleFactory::create() 返回的 QStyle* 在被 QApplication::setStyle() 接管后由 Qt 负责销毁;如果未传入 QApplication,调用方必须自行内存释放(推荐使用 std::unique_ptr)。
  2. 插件加载失败调试
    • 动态加载 DLL/SO 插件时,必须检查 loader.errorString()。常见失败原因为依赖的 Qt 动态库版本不匹配或缺少依赖库。
  3. 结合反射解耦解组
    • Qt 的 QUiLoader / QFormBuilder 通过读取 .ui XML 文件中的类名字符串(如 "QPushButton"),反射实例化对应的控件,是工厂模式在界面解耦上的极致落地。

重点与注意

重点QStyleFactory::create(key)简单工厂模式在 Qt 中的代表,调用方完全解耦了具体的样式类头文件。
重点:Qt 插件机制(QPluginLoader + Q_PLUGIN_METADATA)是工厂方法模式的工业级实现,支持跨 DLL 扩展。
注意:工厂模式解决的是“对象怎么创建”的问题;创建出来的对象生命周期管理依然需要严格遵循 Qt 对象树或 Smart Pointer 规则。


小结

Qt 工厂模式隐藏了复杂对象的初始化细节,实现了物理类型与业务调用的彻底分离。

延伸阅读

基于 VitePress 强力驱动 | 记录技术与生活