C++工厂模式实现变体详解插图

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主题系统,产品存在多个关联系列
  • 模板工厂:高性能库、需要运行时灵活性的场景

记住,模式不是银弹。我曾见过为了用模式而用模式的代码,过度设计反而让简单问题复杂化。关键是理解每个变体解决的问题域,根据实际需求选择最合适的那个。

工厂模式的这些变体,实际上反映了软件设计思维的演进:从简单的封装变化,到面向接口编程,再到处理对象家族的关系。掌握它们不仅能写出更好的代码,更能提升我们对抽象和封装的理解深度。希望今天的分享能帮助你在下次设计对象创建逻辑时,做出更合适的选择。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。