【问题标题】:How to properly implement a factory for templated products如何正确实现模板化产品的工厂
【发布时间】:2016-07-01 08:00:46
【问题描述】:

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

我有一个“产品”层次结构,其中包含一些“特征”,所以我决定对产品使用模板化接口,其中模板参数是“特征”:

这些是特征:

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-tors。

这里是具体的产品:

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&lt;T&gt; 接口产品的指针:

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&lt;Foo, Spam&gt;不能自动转换成它的基类型Product&lt;Foo&gt;...

我不是一个优秀的工厂开发人员,也许我不了解基本原理和概念。任何人都可以帮助更正此代码或这种方法。谢谢!

【问题讨论】:

    标签: c++ templates factory abstract-factory


    【解决方案1】:

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

    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&lt;Foo&gt;* 的函数中返回ProductB&lt;Bar, Spam&gt;*。您似乎认为这些类型在某种程度上通过继承相关,但它们根本不是。 ProductB&lt;Bar, Spam&gt; 实际上是Product&lt;Bar&gt; 的子类——最后一个与Product&lt;Foo&gt; 没有任何关系或可转换为vector&lt;int&gt;vector&lt;float&gt; 相比——即根本没有。具有不同模板参数的模板类是完全不同的。

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

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

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

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

    例如,参见 live demo,我在其中实现了 ProductBase 方法。因此可以编译您的示例,但这要归功于 main 函数中引入的讨厌的static_cast。这是您必须通过适当更改基本接口来解决的问题。

    【讨论】:

    • 好吧,也许我真的误解了模板之间的多态关系,但是您能建议如何解决这个问题吗?是否可以实现创建模板类型对象的工厂?
    • You seem to think that these types are somehow related by inheritance - 当然,我不这样做 :) 我编写这段代码是为了演示一个概念 - 它不会编译,因为在一个函数中返回了两种不同的类型。
    • 查看我对上述答案的更新,包括现场演示
    • 感谢您的建议和演示。我认为重新设计会更好。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 2011-07-04
    • 2020-08-03
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多