
C++工厂模式实现变体详解:从简单工厂到抽象工厂的实战演进
大家好,今天我想和大家深入聊聊C++中工厂模式的几种实现变体。在多年的项目开发中,我发现很多开发者对工厂模式的理解停留在“不就是new一个对象嘛”的层面,但实际上,工厂模式家族有着丰富的层次和适用场景。记得我第一次在大型项目中重构对象创建逻辑时,就是因为没选对合适的工厂变体,导致代码后期难以扩展。今天,我就结合自己的踩坑经验,带大家系统梳理一下。
为什么我们需要工厂模式?
先说说问题的根源。假设我们正在开发一个图形编辑器,需要创建不同的形状:圆形、矩形、三角形。最直接的写法可能是这样的:
if (type == "Circle") {
shape = new Circle();
} else if (type == "Rectangle") {
shape = new Rectangle();
} // ... 更多if else
这段代码的问题很明显:每次新增形状都要修改这段条件判断,违反了开闭原则。而且创建逻辑散落在各处,难以维护。这就是工厂模式要解决的痛点——将对象的创建与使用分离。
变体一:简单工厂(Simple Factory)
简单工厂是最容易理解的入门版本。它通过一个静态方法封装对象的创建逻辑。在实际项目中,我常用它来快速整理散乱的创建代码。
class ShapeFactory {
public:
static Shape* createShape(const std::string& type) {
if (type == "Circle") return new Circle();
if (type == "Rectangle") return new Rectangle();
if (type == "Triangle") return new Triangle();
return nullptr;
}
};
// 使用
Shape* circle = ShapeFactory::createShape("Circle");
实战提示:简单工厂适合创建逻辑不复杂、产品类型固定的场景。但它的缺点也很明显——仍然需要修改工厂类的createShape方法。我在早期项目中就曾因为频繁添加新类型,导致这个函数越来越臃肿。
变体二:工厂方法(Factory Method)
工厂方法模式通过引入“抽象创建者”来解决简单工厂的扩展问题。每个具体产品都由对应的具体工厂创建。这种模式在我设计插件系统时特别有用。
// 抽象产品
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
// 抽象工厂
class ShapeFactory {
public:
virtual Shape* createShape() = 0;
virtual ~ShapeFactory() {}
};
// 具体产品
class Circle : public Shape {
public:
void draw() override { /* 绘制圆形 */ }
};
// 具体工厂
class CircleFactory : public ShapeFactory {
public:
Shape* createShape() override {
return new Circle();
}
};
使用时,我们针对不同形状实例化不同的工厂。这样新增形状时,只需要添加新的具体工厂类,完全不用修改现有代码。
踩坑记录:有一次我忘记将工厂类的析构函数声明为虚函数,导致内存泄漏。记住,有继承关系的基类析构函数一定要是virtual的!
变体三:抽象工厂(Abstract Factory)
当产品存在多个系列时,抽象工厂就派上用场了。比如我们不仅要创建形状,还要创建对应的颜色填充。每个系列(如“红色圆形”、“蓝色矩形”)需要一起创建。
// 抽象产品族
class Color {
public:
virtual void fill() = 0;
virtual ~Color() {}
};
// 抽象工厂
class AbstractFactory {
public:
virtual Shape* createShape() = 0;
virtual Color* createColor() = 0;
virtual ~AbstractFactory() {}
};
// 具体工厂:红色圆形系列
class RedCircleFactory : public AbstractFactory {
public:
Shape* createShape() override { return new Circle(); }
Color* createColor() override { return new Red(); }
};
抽象工厂确保了相关产品对象的兼容性。我在开发跨平台UI组件时深有体会——每个平台(Windows/Mac/Linux)就是一个产品族,抽象工厂保证了同一平台的组件风格一致。
变体四:静态模板工厂(现代C++进阶)
随着C++模板元编程的普及,我们可以实现更灵活的工厂。这种变体在性能要求高的场景下很有优势,因为很多工作可以在编译期完成。
template
class Registry {
public:
using Creator = std::function;
static void register(const std::string& key, Creator creator) {
registry()[key] = creator;
}
static T* create(const std::string& key) {
auto it = registry().find(key);
if (it != registry().end()) {
return it->second();
}
return nullptr;
}
private:
static std::map& registry() {
static std::map instance;
return instance;
}
};
// 注册产品
Registry::register("Circle", [](){ return new Circle(); });
// 创建
Shape* shape = Registry::create("Circle");
这种实现支持运行时动态注册新产品,非常适合需要热插拔功能的框架。不过要注意线程安全问题和静态初始化顺序的问题。
实战选择指南
最后,结合我的经验给大家一些选择建议:
- 简单工厂:快速原型、小型项目,产品类型变化极少
- 工厂方法:框架设计、插件系统,需要良好扩展性
- 抽象工厂:跨平台开发、UI主题系统,产品存在多个关联系列
- 模板工厂:高性能库、需要运行时灵活性的场景
记住,模式不是银弹。我曾见过为了用模式而用模式的代码,过度设计反而让简单问题复杂化。关键是理解每个变体解决的问题域,根据实际需求选择最合适的那个。
工厂模式的这些变体,实际上反映了软件设计思维的演进:从简单的封装变化,到面向接口编程,再到处理对象家族的关系。掌握它们不仅能写出更好的代码,更能提升我们对抽象和封装的理解深度。希望今天的分享能帮助你在下次设计对象创建逻辑时,做出更合适的选择。

评论(0)