我在代码库的不同位置有以下层次结构模式:
enum DerivedType {
A, B, C };
class Base {
public:
static Base* Create(DerivedType t);
};
template <DerivedType T>
class Derived : public Base {
};
Create
方法返回类Derived<A>
,Derived<B>
或Derived<C>
的新对象,具体取决于其参数:
Base* Base::Create(DerivedType t) {
switch (t) {
case A: return new Derived<A>;
case B: return new Derived<B>;
case C: return new Derived<C>;
default: return NULL;
}
}
问题是,有许多这样的Base -> Derived
层次结构,基本上相同的Create()
实现复制粘贴到处。是否有优雅,易于理解的方法来避免重复?
答案 0 :(得分:1)
您可以将其设为模板
template<typename Base, typename Derive, template<Derive> class Derived>
Base* Base::Create(Derive t) {
switch (t) {
case Derive::A: return new Derived<Derive::A>;
case Derive::B: return new Derived<Derive::B>;
case Derive::C: return new Derived<Derive::C>;
default: return nullptr;
}
}
但是假设A
中只有B
,C
和struct enum Derive
。
答案 1 :(得分:1)
您可以使用Java样式枚举,而不是使用C ++样式枚举,其中每个值都是从公共基础派生的类的单个部分。
然后在基类上定义一组纯虚函数,创建所需的派生类风格,并在每个单例中适当地实现它们。
然后您可以使用switch (t) ...
代替t->createDerived()
。
或者更简洁地说:用多态替换switch。
答案 2 :(得分:1)
我们可以抽象出工厂细节,并依靠客户端为我们提供枚举到类类型的映射:
template<typename ENUM, typename T>
struct Factory
{
typedef std::map<ENUM, T*(*)()> map_type;
static map_type factoryMapping_;
static T* Create(ENUM c)
{
return factoryMapping_[c]();
}
static void Init(map_type _mapping)
{
factoryMapping_ = _mapping;
}
};
template<typename ENUM, typename T>
typename Factory<ENUM, T>::map_type Factory<ENUM,T>::factoryMapping_;
现在,客户的工作是为我们提供创建Base*
给定枚举值的方法。
如果您愿意并且能够使用模板抽象出派生类的创建,那么您可以节省大量的输入。
我的意思是,让我们创建一个模板化函数来创建派生类(没有任何真正的正确检查):
template<typename Base, typename Derived>
Base* CreateDerived()
{
return new Derived();
}
现在我可以定义枚举和关联的类层次结构:
enum ClassType {A, B};
struct Foo
{
virtual void PrintName() const
{
std::cout << "Foo\n";
}
};
typedef Factory<ClassType, Foo> FooFactory ;
struct DerivedOne : public Foo
{
virtual void PrintName() const
{
std::cout << "DerivedOne\n";
}
};
struct DerivedTwo : public Foo
{
virtual void PrintName() const
{
std::cout << "DerivedTwo\n";
}
};
然后像这样使用它:
// set up factory
std::map<ClassType, Foo*(*)()> mapping;
mapping[A] = &CreateDerived<Foo, DerivedOne>;
mapping[B] = &CreateDerived<Foo, DerivedTwo>;
FooFactory::Init(mapping);
// Use the factory
Foo* f = FooFactory::Create(A);
f->PrintName();
当然,这会稍微简化您的问题,即将工厂细节移出基类,并忽略孩子们自己模仿的一分钟。根据您的域中为每种类型创建良好CreateDerived
函数的难度,您可能不会最终节省大量的输入。
编辑:我们可以修改Create
函数,以便利用std::map::find返回NULL。为简洁起见,我省略了它。如果你担心性能,那么是的,O(log n)搜索比简单的开关渐近地慢,但我强烈怀疑这将成为热门的道路。