ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

工厂方法模式——C++

工厂方法模式——C++

工厂方法模式(Factory Method)
工厂方法模式是 GoF 设计模式中的一种创建型设计模式。它的核心思想是:将对象的创建交给子类决定,而不是在业务代码中直接使用 new 创建具体对象。
结合你平时使用的 Qt/C++ 开发环境,我们用一个实际的设备控制例子来理解。
一、为什么需要工厂方法模式?
假设你正在开发一套设备控制软件,需要支持多种电源设备,例如 Keithley 2657、2470 和 2636B。
最直接的写法是:

classPowerController{public:voidconnectDevice(inttype){if(type==1){device=newKeithley2657();}elseif(type==2){device=newKeithley2470();}elseif(type==3){device=newKeithley2636B();}}private:PowerDevice*device=nullptr;};

这种写法有什么问题?

  • 每增加一种设备,都需要修改 PowerController。
  • 设备创建逻辑和业务逻辑耦合在一起。
  • 随着设备类型增加,if-else 会越来越多。
  • 不方便扩展和维护。
    工厂方法模式就是为了解决这类问题。
    二、工厂方法模式的结构
    抽象产品 PowerDevice
    统一设备接口:连接、设置电压、读取电流

主要有四个角色:
角色 作用
抽象产品 定义产品的统一接口
具体产品 实现不同产品的功能
抽象工厂 声明创建产品的虚函数
具体工厂 实现创建具体产品的逻辑
三、Qt/C++ 完整代码示例
下面使用 C++14 编写,可以直接在 Qt 项目中使用。

  1. 定义抽象产品
    所有电源设备都遵循同一个接口。
#include<QString>#include<QDebug>#include<memory>classPowerDevice{public:virtual~PowerDevice()=default;virtualvoidconnectDevice()=0;virtualvoidsetVoltage(doublevoltage)=0;};
  1. 实现具体产品
classKeithley2657:publicPowerDevice{public:voidconnectDevice()override{qDebug()<<"连接 Keithley 2657";}voidsetVoltage(doublevoltage)override{qDebug()<<"2657 设置电压:"<<voltage<<"V";}};classKeithley2470:publicPowerDevice{public:voidconnectDevice()override{qDebug()<<"连接 Keithley 2470";}voidsetVoltage(doublevoltage)override{qDebug()<<"2470 设置电压:"<<voltage<<"V";}};

两个设备对外提供相同的接口,但内部实现可以完全不同。
3. 定义抽象工厂

classPowerDeviceFactory{public:virtual~PowerDeviceFactory()=default;virtualstd::unique_ptr<PowerDevice>createDevice()=0;};

这里的 createDevice() 是关键。
它是一个纯虚函数,具体创建什么设备,由子类决定。
4. 实现具体工厂

classKeithley2657Factory:publicPowerDeviceFactory{public:std::unique_ptr<PowerDevice>createDevice()override{returnstd::make_unique<Keithley2657>();}};classKeithley2470Factory:publicPowerDeviceFactory{public:std::unique_ptr<PowerDevice>createDevice()override{returnstd::make_unique<Keithley2470>();}};

每个工厂只负责创建一种具体设备。
5. 客户端调用

intmain(){std::unique_ptr<PowerDeviceFactory>factory=std::make_unique<Keithley2657Factory>();std::unique_ptr<PowerDevice>device=factory->createDevice();device->connectDevice();device->setVoltage(5.0);return0;}

运行结果:
连接 Keithley 2657
2657 设置电压: 5 V

如果要换成 Keithley 2470,只需更换工厂:
std::unique_ptr factory =
std::make_unique();

客户端调用 connectDevice() 和 setVoltage() 的代码不需要改变。
四、工厂方法模式为什么符合开闭原则?
假设以后需要新增 Keithley 2636B。
只需要新增两个类:

classKeithley2636B:publicPowerDevice{public:voidconnectDevice()override{qDebug()<<"连接 Keithley 2636B";}voidsetVoltage(doublevoltage)override{qDebug()<<"2636B 设置电压:"<<voltage<<"V";}};classKeithley2636BFactory:publicPowerDeviceFactory{public:std::unique_ptr<PowerDevice>createDevice()override{returnstd::make_unique<Keithley2636B>();}};

原来的 PowerDevice、PowerDeviceFactory 以及客户端业务逻辑都不需要修改。
这就是开闭原则:

  • 对扩展开放:允许增加新设备、新工厂。
  • 对修改关闭:尽量不修改已经稳定的业务代码。
    需要注意的是,如果客户端仍然在代码里写死 Keithley2657Factory,更换设备时仍然要改配置代码。实际项目可以使用配置文件、依赖注入或上层选择逻辑来决定使用哪个工厂。
    五、工厂方法与简单工厂、抽象工厂有什么区别?
    这三个模式很容易混淆。
    对比 简单工厂 工厂方法 抽象工厂
    创建方式 一个工厂根据条件创建产品 每种具体工厂创建对应产品 一个工厂创建一组相关产品
    是否依赖 if-else 通常有 创建逻辑放在不同子类中 通常由具体工厂决定产品族
    新增产品 通常修改原工厂 通常新增具体工厂 通常新增一整套产品族工厂
    适用场景 产品少、逻辑简单 产品类型多,需要独立扩展 需要创建相互匹配的多类产品
    可以这样记忆:
  • 简单工厂:一个工厂,根据参数决定生产什么。
  • 工厂方法:不同工厂,各自决定生产什么。
  • 抽象工厂:不同工厂,各自生产一整套相关产品。
    六、在 Qt 项目中什么时候适合使用?
    结合设备控制软件,以下情况都可以考虑工厂方法模式:
  • 不同型号的电源、相机或温控器。
  • 不同 TCP/UDP 通信设备的初始化。
  • 不同图像处理算法的创建。
  • 不同硬件厂商的驱动适配。
  • 根据配置动态创建不同的设备实现。
    但如果项目只有两三个固定类,创建逻辑也非常简单,直接构造对象通常更清晰,不必为了使用设计模式而增加工厂类。
    核心总结:工厂方法模式把“创建什么对象”的职责交给具体工厂,让业务代码依赖抽象接口,而不是依赖具体实现。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进