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

时间:2016-07-01 08:00:47

标签: c++ templates factory abstract-factory

我认为这项任务非常普遍,但我无法找到合适的解决方案。

我有"产品的层次结构"它有一些" traits",所以我决定对产品使用模板化接口,其中模板参数是" trait":

这些是特征:

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; }
};

现在我希望有一个统一的机制&#34;以某种方式将字符串传递给某个方法,以某种方式创建产品(在此示例中有两种类型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> ...

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

1 个答案:

答案 0 :(得分:0)

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

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

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

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

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

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

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

例如,请参阅此live demo,我在其中实施ProductBase方法。因此,您的示例可以编译,但仅仅归功于main函数中引入的令人讨厌的static_cast。这是您必须通过适当更改基本界面来解决的问题。