多态性还是属性字典?
Polymorphism or dictionary of properties?
我需要解析一个 XML 并在 C++ 中创建对应于 XML 元素的对象,并将 XML 元素的属性作为属性打包到这些对象中。 XML elements/attributes 和相应的 C++ classes/properties 继承自具有与派生对象共有的属性的基对象。
我正在考虑为公共属性使用基础 class,派生的 classes 只需要定义特定于对象的属性。然而,有人告诉我,在这里使用标准多态性不是一个好主意,因为解决方案不够通用——XML 中的 adding/changing 属性需要 adding/changing C++ 的相应属性classes,需要改动的代码太多。一个更好、更抽象的解决方案是在基础 class 中放置一个属性字典,并通过搜索字典访问各个属性。我想问社区这个建议对生产代码是否更好。
下面是一个示例,其中 vehicle 是基础对象,不同的车辆类型继承自它。 Vehicle 对象具有所有车辆共有的 name
和 weight
属性。这是一个示例 XML 片段:
<car name="Toyota" weight="3500" passengers="4" />
<boat name="Yamaha" weight="3700" draft="16" />
基础class:
class vehicle {
public:
string name;
string weight;
};
派生 classes:
class car : public vehicle {
public:
string passengers;
};
class boat : public vehicle {
public:
string draft;
};
现在,当我的解析器找到我实例化的汽车和船元素时:
boat *b = new boat ();
car *c = new car ();
对于 boat,我可以简单地访问所有成员 b->name
、b->weight
和 b->draft
。在从 XML 实例化 C++ 对象后,我们对每个 属性 执行所有特定于对象的工作,因此目标不仅仅是将 XML 加载到程序中。
替代方法:
#define TAG_XML_ATTRIBUTE_NAME "name"
#define TAG_XML_ATTRIBUTE_WEIGHT "weight"
#define TAG_XML_ATTRIBUTE_PASSENGERS "passengers"
#define TAG_XML_ATTRIBUTE_DRAFT "draft"
. . .
class vehicle {
public:
map<string, string> arguments;
// Get argument by name searching the arguments dictionary.
string GetArgumentOrEmpty(const string& argumentName);
};
class car : public vehicle {
public: // All propeties are in the base class dictionary.
};
class boat : public vehicle {
public: // All propeties are in the base class dictionary.
};
所有派生的 classes 都有 arguments
映射;为了获得 属性 值,我们扫描字典以按名称查找 属性 ,然后读取其值,例如GetArgumentOrEmpty(TAG_XML_ATTRIBUTE_WEIGHT)
。现在的 CPU 很快,所以搜索字典不会明显影响性能。 elements/attributes 的命名空间从结构化(如 class 成员)变为扁平化(如 #define 列表),但它们都在一个地方。代码更抽象;虽然对象处理代码由于额外的间接寻址而变得更加复杂,但如果属性名称发生变化则不需要更改;例如如果XML中的passengers
改为people
,#define看起来会有点别扭,但是使用它的代码不用改:
#define TAG_XML_ATTRIBUTE_PASSENGERS "people"
基于字典的 属性 访问方法是否具有足够的优势以将其用于生产代码而不是使用常规 class 属性?如果是这样,是否仅针对这种特定的 XML 解析情况或用字典搜索替换多态性并用字符串名称替换 class 属性通常是更好的主意?
基于字典的方法确实有几个优点:
- 向 class/object 添加新属性很容易。
- 对象 loading/saving/creation 代码非常通用。
- 可以使属于 class 的属性完全可配置
- 事实上,一些游戏编码专家,例如 Mike McShaffry ("Game Coding Complete") 提倡使用这种 "Component" 架构来表示游戏参与者,而不是使用深度继承层次结构更难维护和丰富。
还有一些不便之处:
- class 特定的处理代码会被 getter 弄得乱七八糟
- 此代码在很大程度上取决于 属性 命名,这使得可配置性变得不那么容易。
- 由于未注意到的打字错误很容易将错误引入系统:编译器不会发现这些不一致,因为它只是垃圾数据。
所以你不得不做出艰难的选择。我的建议是:
- 如果你有复杂的 class 依赖处理代码,那么坚持你原来的方法就不容易出错。编译器知道您正在使用的成员变量的好处是可靠性的保证。
- 如果您只有几个 class 相关例程,并且如果属性主要是描述性的并且以类似的方式处理,那么采用映射方法可能是有意义的
- 如果您需要易于扩展和配置的对象结构,第二种方法也是更灵活的方法。
如果你想要地图,有几点想法:
- 您可以想到一个介于外部 属性 名称和表示它的等效内部整数之间的字典。这将通过避免大量字符串处理开销来加速映射。
- 中的您可以设想使用
unordered_map
来避免过多的字符串比较。
我需要解析一个 XML 并在 C++ 中创建对应于 XML 元素的对象,并将 XML 元素的属性作为属性打包到这些对象中。 XML elements/attributes 和相应的 C++ classes/properties 继承自具有与派生对象共有的属性的基对象。
我正在考虑为公共属性使用基础 class,派生的 classes 只需要定义特定于对象的属性。然而,有人告诉我,在这里使用标准多态性不是一个好主意,因为解决方案不够通用——XML 中的 adding/changing 属性需要 adding/changing C++ 的相应属性classes,需要改动的代码太多。一个更好、更抽象的解决方案是在基础 class 中放置一个属性字典,并通过搜索字典访问各个属性。我想问社区这个建议对生产代码是否更好。
下面是一个示例,其中 vehicle 是基础对象,不同的车辆类型继承自它。 Vehicle 对象具有所有车辆共有的 name
和 weight
属性。这是一个示例 XML 片段:
<car name="Toyota" weight="3500" passengers="4" />
<boat name="Yamaha" weight="3700" draft="16" />
基础class:
class vehicle {
public:
string name;
string weight;
};
派生 classes:
class car : public vehicle {
public:
string passengers;
};
class boat : public vehicle {
public:
string draft;
};
现在,当我的解析器找到我实例化的汽车和船元素时:
boat *b = new boat ();
car *c = new car ();
对于 boat,我可以简单地访问所有成员 b->name
、b->weight
和 b->draft
。在从 XML 实例化 C++ 对象后,我们对每个 属性 执行所有特定于对象的工作,因此目标不仅仅是将 XML 加载到程序中。
替代方法:
#define TAG_XML_ATTRIBUTE_NAME "name"
#define TAG_XML_ATTRIBUTE_WEIGHT "weight"
#define TAG_XML_ATTRIBUTE_PASSENGERS "passengers"
#define TAG_XML_ATTRIBUTE_DRAFT "draft"
. . .
class vehicle {
public:
map<string, string> arguments;
// Get argument by name searching the arguments dictionary.
string GetArgumentOrEmpty(const string& argumentName);
};
class car : public vehicle {
public: // All propeties are in the base class dictionary.
};
class boat : public vehicle {
public: // All propeties are in the base class dictionary.
};
所有派生的 classes 都有 arguments
映射;为了获得 属性 值,我们扫描字典以按名称查找 属性 ,然后读取其值,例如GetArgumentOrEmpty(TAG_XML_ATTRIBUTE_WEIGHT)
。现在的 CPU 很快,所以搜索字典不会明显影响性能。 elements/attributes 的命名空间从结构化(如 class 成员)变为扁平化(如 #define 列表),但它们都在一个地方。代码更抽象;虽然对象处理代码由于额外的间接寻址而变得更加复杂,但如果属性名称发生变化则不需要更改;例如如果XML中的passengers
改为people
,#define看起来会有点别扭,但是使用它的代码不用改:
#define TAG_XML_ATTRIBUTE_PASSENGERS "people"
基于字典的 属性 访问方法是否具有足够的优势以将其用于生产代码而不是使用常规 class 属性?如果是这样,是否仅针对这种特定的 XML 解析情况或用字典搜索替换多态性并用字符串名称替换 class 属性通常是更好的主意?
基于字典的方法确实有几个优点:
- 向 class/object 添加新属性很容易。
- 对象 loading/saving/creation 代码非常通用。
- 可以使属于 class 的属性完全可配置
- 事实上,一些游戏编码专家,例如 Mike McShaffry ("Game Coding Complete") 提倡使用这种 "Component" 架构来表示游戏参与者,而不是使用深度继承层次结构更难维护和丰富。
还有一些不便之处:
- class 特定的处理代码会被 getter 弄得乱七八糟
- 此代码在很大程度上取决于 属性 命名,这使得可配置性变得不那么容易。
- 由于未注意到的打字错误很容易将错误引入系统:编译器不会发现这些不一致,因为它只是垃圾数据。
所以你不得不做出艰难的选择。我的建议是:
- 如果你有复杂的 class 依赖处理代码,那么坚持你原来的方法就不容易出错。编译器知道您正在使用的成员变量的好处是可靠性的保证。
- 如果您只有几个 class 相关例程,并且如果属性主要是描述性的并且以类似的方式处理,那么采用映射方法可能是有意义的
- 如果您需要易于扩展和配置的对象结构,第二种方法也是更灵活的方法。
如果你想要地图,有几点想法:
- 您可以想到一个介于外部 属性 名称和表示它的等效内部整数之间的字典。这将通过避免大量字符串处理开销来加速映射。
- 中的您可以设想使用
unordered_map
来避免过多的字符串比较。