假设编写一个图形编程程序,所有的图形类都有共同的父类Graphics。Graphics类中提供了进行序列化的接口Serialize()。
现在当用户使用该图形编辑器创建了多个图形对象(即创建了多个Graphics的子类的实例,这些实例可以通过一个chain of responsibility连接起来),并希望将该图形保存到一个文件里面。我们需要遍历该职责链,依次调用每个实例的Serialize方法。
问题随之产生:我们如何读取这些记录呢?我们怎么知道当前的读取的数据是哪个子类的实例的数据呢?产生这个问题的根本在于我们无法将类的类型信息保存到文件中
一个解决办法是:在存入文件的时侯,每个类都写入一个唯一的ID信息;那么在读入的时侯,我们可以通过该ID信息知道这是哪个类的实例的数据。大概的代码可能是这样的:
void Serialize(CArchive & ar){
if(ar.IsLoading){
ar>>id;
switch(id){
case SUB_CLASS1:
newInstance=new SubClass1;
break;
case SUB_CLASS2:
newInstance=new SubClass2;
break;
// ...
}
newInstance.Serialize(ar);
}
}
但是这似乎并不是一个好方法,因为每次当我们新增加一个子类的时侯,都需要为其手工增加一个id,而且需要在上面代码中的switch...case结构中增加一个相应的入口。当子类的数目众多,或者子类的种类在不断变化的时候,这样做就非常不可靠了。
其实可以通过类型注册来解决这个问题。
首先解决ID的问题,这个可以借助Loki库的Typelist(参见我的另外一篇文章)。
我们可以使用一个数据结构来保存所有的子类的信息(通过保存一个该类的实例的方式来保存该类的信息)
class AbstractBuilder{};
template <class T> class Builder:public AbstractBuilder
{
public:
T* clone(){ return new T;}
};
//...
void RegsiterClassInfo()
{
classInfoList.push_back(Builder<SubClass1> tmp1);
classInfoList.push_back(Builder<SubClass2> tmp2);
// ...
}
void Serialize(CArchive& ar)
{
ar>>id;
for(iter=classInfoList.begin();iter!=classInfoList.end();++iter){
if(id == iter->id){
newInstance= iter->Clone();
break;
}
}
newInstance->Serialize(ar);
}