我的问题或多或少与Need a design pattern to remove enums and switch statement in object creation的问题相同但是我没有看到抽象工厂模式在这里很适合。
我目前正计划重构/重新实现一些现有的DAL / ORM混合库。在现有代码中的某处,代码如下所示:
class Base
{
static Base * create(struct Databasevalues dbValues)
{
switch(dbValues.ObjectType)
{
case typeA:
return new DerivedA(dbValues);
break;
case typeB:
return new DerivedB(dbValues);
break;
}
}
}
class DerivedA : public Base
{
// ...
}
class DerivedB : public Base
{
// ...
}
因此,负责数据库通信的库使用有关数据库实体的所有信息填充结构,然后调用上述create()方法以在ORM中实际创建相应的对象。 但我不喜欢基类知道所有派生类的想法,我也不喜欢switch语句。我还想避免创建另一个类只是为了创建这些对象。您如何看待当前的方法?您将如何实现此功能?
答案 0 :(得分:6)
这已经在这里讨论了数百万次。如果您不想创建单独的工厂类,则可以执行此操作。
class Base
{
public:
template <class T>
static void Register (TObjectType type)
{
_creators[type] = &creator<T>;
}
static Base* Create (TObjectType type)
{
std::map <TObjectType, Creator>::iterator C = _creators.find (type);
if (C != _creators.end())
return C->second ();
return 0;
}
private:
template <class T>
static Base* creator ()
{
return new T;
}
private:
typedef Base* (::*Creator) ();
static std::map <TObjectType, Creator> _creators;
};
int main ()
{
Base::Register <Derived1> (typeA);
Base::Register <Derived2> (typeB);
Base* a = Base::Create (typeA);
Base* b = Base::Create (typeB);
}
答案 1 :(得分:3)
假设您使用映射替换交换机,例如map<ObjectType, function<Base* (DatabaseValues&)>>
。
现在,工厂(可能存在或不存在于基类中)不需要知道所有子类。
但是,地图必须以某种方式填充。这意味着要么填充它(因此你的知道所有子类问题刚被从一个地方推到另一个地方),或者你需要子类来使用静态初始化来在地图中注册它们的工厂函数。 / p>
答案 2 :(得分:1)
无论你做什么,你都需要切换案例或其他一些只隐藏类似逻辑的构造。
然而,您可以而且应该做的是从您的Base中删除create方法 - 您完全正确它不应该知道它的派生方法。此逻辑属于另一个实体,例如工厂或控制器。
答案 3 :(得分:1)
只是不要使用枚举。它们不是OO构造,这就是为什么JAVA在开始时没有它们(不幸的是压力太大而无法添加它们)。
考虑代替这样的枚举:
enum Types {
typeA,
typeB
};
这种结构,不需要切换(在我看来是另一种非OO结构)和地图:
<强> types.h中强>
class Base;
class BaseFactory {
public:
virtual Base* create() = 0;
};
class Types {
public:
// possible values
static Types typeA;
static Types typeB;
// just for comparison - if you do not need - do not write...
friend bool operator == (const Types & l, const Types & r)
{ return l.unique_id == r.unique_id; }
// and make any other properties in this enum equivalent - don't add them somewhere else
Base* create() { return baseFactory->create(); }
private:
Types(BaseFactory* baseFactory, unsigned unique_id);
BaseFactory* baseFactory;
unsigned unique_id; // don't ever write public getter for this member variable!!!
};
<强> Types.cpp 强>
#include "Types.h"
#include "Base.h"
#include "TypeA.h"
#include "TypeB.h"
namespace {
TypeAFactory typeAFactory;
TypeBFactory typeAFactory;
unsigned unique_id = 0;
}
Types Types::typeA(&typeAFactory, unique_id++);
Types Types::typeA(&typeBFactory, unique_id++);
所以你的例子(如果你真的需要这个功能):
class Base
{
static Base * create(struct Databasevalues dbValues)
{
return dbValues.ObjectType.create();
}
};
缺少零件应易于实施。