代码之家  ›  专栏  ›  技术社区  ›  lexxa2000

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

  •  0
  • lexxa2000  · 技术社区  · 8 年前

    我认为这项任务很常见,但我找不到合适的解决办法。

    我有一个“products”层次结构,其中有一些“trait”,所以我决定对产品使用模板化接口,其中模板参数是“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_;
    };
    

    我需要使用额外的继承级别,因为 ProductA ProductB -他们有不同的角色。

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

    现在我想有一个“统一”创建产品的机制(在这个例子中有两种类型 ProductA1 ProductB1 )不知怎么的,字符串被传递到了某个方法中。显然我需要工厂。。。

    产品A 产品B )这样我就可以创建通过模板参数传递其类型的对象:

    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 回复  |  直到 8 年前
        1
  •  0
  •   Smeeheey    8 年前

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

    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 ,其中我实现了 产品基础 方法因此,您的示例可以编译,但这要归功于一个 static_cast 主要功能介绍。这是您必须通过适当更改基本接口来解决的问题。