You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用空基类强制继承实现抽象工厂是否为不良实践?

关于空基类实现抽象工厂的看法与改进方案

你提到的这种用empty_base_factory强制统一类型的做法确实比较生硬,本质上是在绕开C++静态类型系统的限制——因为factoryA::make_object和factoryB::make_object返回不同的智能指针类型(shared_ptr<A> vs shared_ptr<B>),没法直接定义一个包含纯虚make_object方法的基类接口。这种方式的弊端很明显:

  • 空基类没有任何约束能力,完全依赖开发者手动保证所有派生工厂都实现了make_object,很容易出错;
  • 代码可读性差,其他维护者看到empty_base_factory会困惑它的实际作用;
  • 从abstract_factory获取工厂后,还需要手动向下转型才能调用make_object,既繁琐又不安全。

下面给你几个更合理的改进方向:

1. 按产品类型拆分抽象工厂(最符合经典设计模式)

经典抽象工厂模式的核心是创建一组相关或相互依赖的对象,如果你的A和B是不相关的产品系列,完全可以把它们的工厂分开管理:

// 针对A系列的抽象工厂
struct AbstractFactoryA {
    virtual ~AbstractFactoryA() = default;
    virtual std::shared_ptr<A> make_object() = 0;
};

struct FactoryA : public AbstractFactoryA {
    std::shared_ptr<A> make_object() override {
        return std::make_shared<DerivedA>(); // DerivedA是A的派生类
    }
};

// 针对B系列的抽象工厂
struct AbstractFactoryB {
    virtual ~AbstractFactoryB() = default;
    virtual std::shared_ptr<B> make_object() = 0;
};

struct FactoryB : public AbstractFactoryB {
    std::shared_ptr<B> make_object() override {
        return std::make_shared<DerivedB>(); // DerivedB是B的派生类
    }
};

// 可以用一个总工厂来管理这两个抽象工厂(如果需要的话)
class GlobalFactory {
private:
    std::shared_ptr<AbstractFactoryA> factory_a;
    std::shared_ptr<AbstractFactoryB> factory_b;
public:
    GlobalFactory() : factory_a(std::make_shared<FactoryA>()), factory_b(std::make_shared<FactoryB>()) {}
    
    std::shared_ptr<AbstractFactoryA> get_factory_a() { return factory_a; }
    std::shared_ptr<AbstractFactoryB> get_factory_b() { return factory_b; }
};

这种方式完全利用了C++的多态特性,接口清晰,类型安全,也符合单一职责原则。

2. 用类型擦除统一工厂接口(适合必须统一管理的场景)

如果一定要把所有工厂放在同一个容器里,可以用类型擦除技术,通过std::any或std::variant统一返回类型:

struct BaseFactory {
    virtual ~BaseFactory() = default;
    virtual std::any make_object() = 0;
};

struct FactoryA : public BaseFactory {
    std::any make_object() override {
        return std::make_shared<DerivedA>();
    }
};

struct FactoryB : public BaseFactory {
    std::any make_object() override {
        return std::make_shared<DerivedB>();
    }
};

class AbstractFactory {
private:
    std::map<uint8_t, std::shared_ptr<BaseFactory>> iv_factories;
public:
    AbstractFactory() {
        iv_factories[0] = std::make_shared<FactoryA>();
        iv_factories[1] = std::make_shared<FactoryB>();
    }

    template<typename Product>
    std::shared_ptr<Product> create_object(uint8_t key) {
        auto it = iv_factories.find(key);
        if (it == iv_factories.end()) {
            throw std::invalid_argument("Invalid factory key");
        }
        // 类型转换,需要确保key对应的工厂确实返回Product类型
        return std::any_cast<std::shared_ptr<Product>>(it->second->make_object());
    }
};

使用时直接调用create_object指定目标类型即可:

AbstractFactory af;
auto a = af.create_object<A>(0);
auto b = af.create_object<B>(1);

这种方式的代价是运行时类型转换的开销,以及需要开发者保证key和产品类型的对应关系,但至少比空基类的方式更清晰。

3. 用模板简化工厂注册(最简洁的实现)

如果不想手动编写每个工厂类,可以用模板函数直接注册产品的创建逻辑,完全省去中间工厂类:

class AbstractFactory {
private:
    using CreateFunc = std::function<std::any()>;
    std::map<uint8_t, CreateFunc> creators;
public:
    // 注册产品类型和对应的key
    template<typename Product>
    void register_product(uint8_t key) {
        creators[key] = []() {
            return std::make_shared<Product>();
        };
    }

    // 根据key创建指定类型的产品
    template<typename Product>
    std::shared_ptr<Product> create_object(uint8_t key) {
        auto it = creators.find(key);
        if (it == creators.end()) {
            throw std::invalid_argument("Invalid product key");
        }
        return std::any_cast<std::shared_ptr<Product>>(it->second());
    }
};

使用时只需要注册派生类:

AbstractFactory af;
af.register_product<DerivedA>(0);
af.register_product<DerivedB>(1);

auto a = af.create_object<A>(0);
auto b = af.create_object<B>(1);

这种方式代码量最少,灵活性也最高,不需要额外的工厂类或空基类,非常适合快速实现工厂模式。


内容的提问来源于stack exchange,提问作者Andre Marin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 03:56:48