如何为模板化产品正确实施工厂
How to properly implement a factory for templated products
我认为这个任务很常见,但我找不到合适的解决方案。
我有一个 "products" 的层次结构,其中有一些 "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_;
};
我需要使用额外的继承级别,因为 ProductA
和 ProductB
的接口不同——它们有不同的 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; }
};
现在我想有一个 "unified" 创建产品的机制(在这个例子中有两种类型 ProductA1
和 ProductB1
)以某种方式将字符串传递到某个方法中。显然我需要工厂...
所以我为层次结构的不同分支实现了工厂(以创建类型 ProductA
和 ProductB
的对象),这样我就可以创建通过模板参数传递其类型的对象:
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);
}
};
有了这些工厂,我就必须有一个工厂来构造必要类型的产品和 return 指向 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
问题的症结在于您正试图从声明为 return Product<Foo>*
的函数中 return 一个 ProductB<Bar, Spam>*
。您似乎认为这些类型通过继承以某种方式相关,但它们根本不是。 ProductB<Bar, Spam>
实际上是 Product<Bar>
的子 class - 最后一个与 Product<Foo>
没有任何关系或可转换为 vector<int>
到 vector<float>
- 即根本不是。具有不同模板参数的模板 class 是完全不同的。
所以要么你在你的代码中犯了一个错误,并且正在尝试 return a ProductB<Bar,Spam>*
而实际上你应该尝试 return a ProductB<Foo,Spam>*
(比方说),或者您误解了具有不同模板参数的模板继承层次结构之间的多态关系(或缺乏多态关系)。
更新: 回应您的评论:可以创建一个工厂来创建不同模板类型的对象,只要它们在类型层次结构中具有共同的祖先即可可以用作 return 类型。在您的情况下,这至少需要在某种程度上重新设计。一种方法是为 Product<T>
创建一个基础 class,比如 ProductBase
,它不依赖于模板:
template <class T>
class Product : public ProductBase
{
...
};
那么你的create
函数的return类型可以是ProductBase*
。同样,您可以从 Product
本身的定义中取出模板。无论哪种方式,这都需要对界面进行一些进一步的更改才能使其有用。
例如,请参阅此 live demo,我在其中实施了 ProductBase
方法。因此,您的示例可以编译,但这要归功于 main 函数中引入的讨厌的 static_cast
。这是您必须通过适当更改基本界面来解决的问题。
我认为这个任务很常见,但我找不到合适的解决方案。
我有一个 "products" 的层次结构,其中有一些 "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_;
};
我需要使用额外的继承级别,因为 ProductA
和 ProductB
的接口不同——它们有不同的 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; }
};
现在我想有一个 "unified" 创建产品的机制(在这个例子中有两种类型 ProductA1
和 ProductB1
)以某种方式将字符串传递到某个方法中。显然我需要工厂...
所以我为层次结构的不同分支实现了工厂(以创建类型 ProductA
和 ProductB
的对象),这样我就可以创建通过模板参数传递其类型的对象:
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);
}
};
有了这些工厂,我就必须有一个工厂来构造必要类型的产品和 return 指向 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
问题的症结在于您正试图从声明为 return Product<Foo>*
的函数中 return 一个 ProductB<Bar, Spam>*
。您似乎认为这些类型通过继承以某种方式相关,但它们根本不是。 ProductB<Bar, Spam>
实际上是 Product<Bar>
的子 class - 最后一个与 Product<Foo>
没有任何关系或可转换为 vector<int>
到 vector<float>
- 即根本不是。具有不同模板参数的模板 class 是完全不同的。
所以要么你在你的代码中犯了一个错误,并且正在尝试 return a ProductB<Bar,Spam>*
而实际上你应该尝试 return a ProductB<Foo,Spam>*
(比方说),或者您误解了具有不同模板参数的模板继承层次结构之间的多态关系(或缺乏多态关系)。
更新: 回应您的评论:可以创建一个工厂来创建不同模板类型的对象,只要它们在类型层次结构中具有共同的祖先即可可以用作 return 类型。在您的情况下,这至少需要在某种程度上重新设计。一种方法是为 Product<T>
创建一个基础 class,比如 ProductBase
,它不依赖于模板:
template <class T>
class Product : public ProductBase
{
...
};
那么你的create
函数的return类型可以是ProductBase*
。同样,您可以从 Product
本身的定义中取出模板。无论哪种方式,这都需要对界面进行一些进一步的更改才能使其有用。
例如,请参阅此 live demo,我在其中实施了 ProductBase
方法。因此,您的示例可以编译,但这要归功于 main 函数中引入的讨厌的 static_cast
。这是您必须通过适当更改基本界面来解决的问题。