如何正确实现模板化产品的工厂

How to properly implement a factory for templated products

本文关键字:工厂 何正确 实现      更新时间:2023-10-16

我认为这个任务是很常见的,但我找不到一个合适的解决方案。

我有一个层次结构的"产品",其中有一些"特征",所以我决定使用模板接口的产品,其中模板参数是"特质":

这些是特征:

struct Foo {
    static std::string get_name() { return "Foo"; }
    Foo(int a) : a_(a) {}
    int operator()() const { return a_; }
private:
    int a_;
};
struct Bar {
    static std::string get_name() { return "Bar"; }
    Bar(int a, int b) : a_(a), b_(b) {}
    int operator()() const { return a_ + b_; }
private:
    int a_;
    int b_;
};
struct Spam {
    Spam(int a, int b) : a_(a), b_(b), c_(0) {}
    void operator()() { c_++; }
private:
    int a_;
    int b_;
    int c_;
};

这些是产品层次结构:

template <class T>
class Product {
public:
    typedef T T_type;
    virtual T get() = 0;
    virtual ~Product() {}
};
template <class T>
class ProductA : public Product<T> {
    typedef Product<T>   base_type;
public:
    ProductA(int a) : a_(a) {}
    virtual ~ProductA() {}
    virtual T get() { 
        return T(a_); 
    }
private:
    int a_;
};
template <class T, class U>
class ProductB : public Product<T> {
    typedef Product<T>                                  base_type;
public:
    typedef U                                           U_type;
    ProductB(int a, int b) : a_(a), b_(b) {}
    virtual ~ProductB();
    virtual T get() { 
        init(); // U is being used here
        return T(a_, b_); 
    }
protected:
    void init() {}
private:
    int a_;
    int b_;
};

我需要使用额外的继承级别,因为ProductAProductB的不同接口——它们有不同的c-tor。

以下是具体产品:

class ProductA1 : public ProductA<Foo> {
    typedef ProductA<Foo>  base_type;
public:
    ProductA1(int a) : base_type(a) { std::cout << "A1 created" << std::endl; }
    virtual ~ProductA1() { std::cout << "A1 deleted" << std::endl; }
};
class ProductB1 : public ProductB<Bar, Spam> {
    typedef ProductB<Bar, Spam>  base_type;
public:
    ProductB1(int a, int b) : base_type(a, b) { std::cout << "B1 created" << std::endl; }
    virtual ~ProductB1() { std::cout << "B1 deleted" << std::endl; }
};

现在我想有一个"统一"创建产品的机制(在这个例子中有两个类型ProductA1ProductB1),以某种方式将字符串传递到某个方法中。显然我需要工厂…

因此,我为层次结构的不同分支实现了工厂(以创建ProductAProductB类型的对象),以便我可以创建通过模板参数传递其类型的对象:

template <class P>
struct ProductAFactory {
    typedef typename P::T_type      T_type;
    typedef ProductA<T_type>        product_type;
    static 
    product_type* create(int a) { 
        return new P(a); 
    }
};

template <class P>
struct ProductBFactory {
    typedef typename P::T_type      T_type;
    typedef typename P::U_type      U_type;
    typedef ProductB<T_type,
                     U_type>        product_type;
    static
    product_type* create(int a, int b) {
        return new P(a, b);
    }
};

有了这些工厂,我必须有一个工厂应该构造必要类型的产品,并返回一个指向Product<T>接口的产品的指针:

template <class T>
class ProductFactory {
public:
    static 
    Product<T>*
    create(const std::string& product_name, 
           const int a, 
           const int b) {
        const std::string product_a1 = "A1";
        const std::string product_b1 = "B1";
        if (product_name == product_a1)
            return ProductAFactory<ProductA1>::create(a);
        else if (product_name == product_b1)
            return ProductBFactory<ProductB1>::create(a, b); // (*) <--- compiler error
        else
            throw std::runtime_error("Unsupported product: " + product_name);
    }
}; 

所有这些代码的目的是这样使用:

void main() {
    typedef Foo T;
    std::shared_ptr<Product<T>> p(ProductFactory<T>::create("A1", 1, 1));
    T t = p->get(); 
    std::cout << t.get_name() << ": " << t() << std::endl;
}

在这里我遇到了一个编译这段代码的问题——错误是return value type does not match the function type(*)。似乎ProductB<Foo, Spam>不能自动转换为它的基类型Product<Foo>

我不是一个好的工厂开发人员,也许我不理解基本的原则和概念。有人能帮助纠正这个代码或这种方法吗?谢谢!

当重新创建这个时,我得到的错误是(与您发布的略有不同):

error: cannot convert 'ProductBFactory<ProductB1>::product_type* {aka ProductB<Bar, Spam>*}' to 'Product<Foo>*' in return
         return ProductBFactory<ProductB1>::create(a, b); // (*) <--- compiler error

问题的关键在于,您试图从声明返回Product<Foo>*的函数返回ProductB<Bar, Spam>*。你似乎认为这些类型在某种程度上与继承有关,但它们根本不是。ProductB<Bar, Spam>实际上是Product<Bar>的一个子类——最后一个与Product<Foo>没有任何关系或可转换,就像vector<int>vector<float>没有任何关系一样——也就是说,根本没有。具有不同模板参数的模板类是完全不同的。

所以要么你在你的代码中犯了一个错误,并试图返回一个ProductB<Bar,Spam>*,而实际上你应该试图返回一个ProductB<Foo,Spam>*(说),或者你误解了模板继承层次结构之间的多态关系(或缺乏)与不同的模板参数。

UPDATE:回复你的评论:可以创建一个工厂来创建不同模板类型的对象,只要它们在类型层次结构中有一个共同的祖先,可以用作返回类型。在您的情况下,这需要至少在某种程度上重新设计。一种方法是为Product<T>创建一个基类,比如ProductBase,它不依赖于模板:

template <class T>
class Product : public ProductBase
{
    ...
};

那么你的create函数的返回类型可以是ProductBase*。同样,您可以从Product本身的定义中取出模板。无论哪种方式,这都需要对接口进行一些进一步的更改以使其有用。

例如,请参阅这个现场演示,我在其中实现了ProductBase方法。因此,您的示例可以编译,但这只是由于在主函数中引入了一个讨厌的static_cast。这类问题必须通过适当地更改基本接口来解决。