我有一个工厂类来构建基类B的对象。 使用此工厂的对象(D)接收表示实际类型的字符串列表。 什么是正确的实现:有关工厂模式的问题
- 工厂接收枚举(并使用开关创建函数内部)和d负责将字符串转换为枚举。
- 工厂收到一个字符串并检查匹配到一组有效字符串(使用ifs')
- 其他实现我没有想到。
我有一个工厂类来构建基类B的对象。 使用此工厂的对象(D)接收表示实际类型的字符串列表。 什么是正确的实现:有关工厂模式的问题
您可以将所有匹配的字符串放在集合或列表中,并检查它是否包含字符串而不是写入ifs/switches。
我会分开字符串转换为枚举成一个不同的对象。这可以很容易地通过地图顺便解决。但是错误处理等仍然是D和工厂都不应该担心的事情。
然后要么D调用转换器来获得它的枚举,或者它已经预先转换,所以D只需要将枚举传递给工厂。 (顺便说一句,工厂会更好地使用地图,而不是内部的开关)。
这引发了一个问题:你是否真的需要枚举(在D和工厂以外的地方)?如果没有,那么也许enum可能被排除在图片之外,并且您可以使用映射将字符串直接转换为类型(即 - 因为C++不支持动态类加载 - 为函数创建必要的具体类型实例的对象您)。一个粗略的例子(我没有一个IDE来测试它,容忍我是否有任何错误):
// Function type returning a pointer to B
typedef (B*)(*func)() StaticConstructor;
// Function creating instances of subclass E
B* createSubclassE() {
return new E(...);
}
// Function creating instances of subclass F
B* createSubclassF() {
return new F(...);
}
// Mapping from strings to constructor methods creating specific subclasses of B
map<string, StaticConstructor> factoryMap;
factoryMap["E"] = &createSubclassE;
factoryMap["F"] = &createSubclassF;
当然,创建的实例也应妥善处理 - 在生产代码,返回的对象可能是例如封闭在auto_ptr
。但我希望这个简短的例子足以向你展示基本的想法。这里是a tutorial如果你想要更多...
你是什么意思的字符串类型映射? 那张地图应该是什么样子? – amitlicht 2010-03-13 16:34:21
@eriks我添加了一个例子。 – 2010-03-13 21:16:17
我的项目VC++/Qt
有大量的XML文件,其中包含字符串的Enum表示到源。因此,对于每一个枚举
我们与重载运营商QString
<>Enum
包装:
enum DataColumnTypeEnum
{
DataColumnTypeNotSet,
ColumnBinary,
ColumnBoolean,
ColumnDate,
ColumnDateTime,
ColumnNumber,
ColumnFloat,
ColumnPrimary,
ColumnString,
ColumnText,
};
class DataColumnType
{
public:
DataColumnType();
DataColumnType(DataColumnTypeEnum);
DataColumnType(const QString&);
DataColumnType& operator = (DataColumnTypeEnum);
DataColumnType& operator = (const QString&);
operator DataColumnTypeEnum() const;
operator QString() const;
private:
DataColumnTypeEnum type;
};
DataColumnType& DataColumnType::operator = (const QString& str)
{
str.toLower();
if(str.isEmpty()) type = DataColumnTypeNotSet;
else if(str == "binary") type = ColumnBinary;
else if(str == "bool") type = ColumnBoolean;
else if(str == "date") type = ColumnDate;
else if(str == "datetime") type = ColumnDateTime;
else if(str == "number") type = ColumnNumber;
else if(str == "float") type = ColumnFloat;
else if(str == "primary") type = ColumnPrimary;
else if(str == "string") type = ColumnString;
else if(str == "text") type = ColumnText;
return *this;
}
但在去年上市的做法是非常难看。
更好地创建一个静态哈希表或字典,并查找。
正常的方法是让你的工厂作为一个单身人士。然后每个基于类B的类在静态初始化时注册它的创建函数和名称。这通常是用宏来完成的。工厂然后可以创建这些名称的快速哈希表来创建函数。等等......你得到漂移。
我不同意。工厂不一定是单身人士。事实上,由于Singletons使单元测试变得困难,所以除非确实需要,否则最好避免它们。 – 2010-03-13 13:59:39
谢谢佩特(评论+1)。我同意工厂本身不需要是单身,但类的列表必须以某种方式存在,我宁愿用静态初始化来构造它。而不是在一个函数或数据文件中。在大型团队中工作时,会阻止人们与其他文件进行交锋。根据需要,其他方法可能更合乎需要。 – 2010-03-13 18:17:34
我个人使用增强的枚举,因为我一直发现C++缺乏枚举:像Type 3 - method -begin
这样的消息没有太多的信息。
要这样,我用一个简单的模板类:
template <class Holder>
class Enum
{
public:
typedef typename Holder::type enum_type;
Enum(): mValue(Invalid()) {}
Enum(enum_type i): mValue(Get(i)) {}
explicit Enum(const std::string& s): mValue(Get(s)) {}
bool isValid() const { return mValue != Invalid(); }
enum_type getValue() const { return mValue->first; }
private:
typedef typename Holder::mapping_type mapping_type;
typedef typename mapping_type::const_iterator iterator;
static const mapping_type& Mapping() { static mapping_type MMap = Holder::Initialize(); return MMap; }
static iterator Invalid() { return Mapping().end(); }
static iterator Get(enum_type i) { // search }
static iterator Get(const std::string& s) { // search }
iterator mValue;
};
你定义Holder
像这样:
struct Example
{
typedef enum {
Value1,
Value2,
Value3
} type;
typedef std::vector< std::pair< type, std::string > > mapping_type;
static mapping_type Initialize() {
return builder<mapping_type>()(Value1,"Value1")(Value2,"Value2")(Value3,"Value3");
}
};
您可以定义一个宏吧:
DEFINE_ENUM(Example, (Value1)(Value2)(Value3))
但我把这个实现作为练习(Boost.Preprocessor
是你的朋友)。
很酷的事情是使用它!
int main(int argc, char* argv[])
{
std::string s;
std::cin >> s;
Enum<Example> e(s);
switch(e.getValue())
{
case Example::Value1:
case Example::Value2:
++e;
case Example::Value3:
std::cout << e << std::endl;
default:
}
}
工厂仍然必须根据字符串构造正确的对象。它必须做如果/开关 – amitlicht 2010-03-13 12:46:10
有可能有字符串 - > creator函数比ifs /开关好得多。 – 2010-03-13 12:52:07
我认为切换枚举是不可避免的。无论如何,无论如何,你都会明确或隐含地做到这一点。 – abatishchev 2010-03-13 12:58:04