当我第一次看到使用一个字符串描述类型来创建对象时觉得很稀奇,我在猜想,是不是内部使用超多的case,通过判断字符串来创建对应的对象。但是这样的情况是静态的,所有的字符串都已经被预置了,当有一个新的类型出现,那么怎么又神奇的扩展了呢?伪代码如下:
Animal *cat = Animal::Create(“Cat”); // 同上
class RuntimeClassT
{
public:
RuntimeClassT(t_CreateObjectFun _fun, Cstr _name)
: CreateObject(_fun)
, m_name(_name)
, m_next(s_first)
{
s_first = this;
}
static RuntimeClassT* FromClassNameS(Cstr _name)
{
for (RuntimeClassT* rc = s_first; rc != 0; rc = rc->m_next)
{
if (strcmp(_name, rc->m_name) == 0)
return rc;
}
return Null;
}
t_CreateObjectFun CreateObject;
private:
Cstr m_name;
RuntimeClassT* m_next;
static RuntimeClassT* s_first;
};
template<typename t_CreateObjectFun>
RuntimeClassT<t_CreateObjectFun>* RuntimeClassT<t_CreateObjectFun>::s_first = 0;
明白了RuntimeClassT怎么形成单向链表,那么接下来就看看这单向链表中的元素都含些什么内容。首先是m_name,这是一个c风格的字符串指针,什么作用?好吧,现在看看FromClassNameS这个静态方法。这个方法接受一个c字符串,利用这个c字符串查找在单向链表中有相同内容的RuntimeClassT对象,这个内容就是m_name。
既然从单向链表中找到了需要的RuntimeClassT对象,那么接下来真真的肉戏就是CreateObject了。为了能创建对象CreateObject应该是一个函数指针,或者是一个仿函数。说了这么多,似乎根本还不知道RuntimeClassT有什么作用。因此我写一个示例代码,说明一下:
{
public:
Animal(const String& _name) : m_name(_name){};
virtual ~Animal(void) {};
virtual void Talk(void) = 0;
// 定义CreateObject的类型。
typdef Animal* (*Factory_f)(const String&);
// 实例化RuntimeClassT模板。
typedef RuntimeClassT<Factory_f> RuntimeFactory;
// 实现运行时创建对象的关键代码。
static Animal* CreateS(Cstr _classname, const String& _name)
{
// 查找指定的运行时类。
RuntimeFactory* factory = RuntimeFactory::FromClassName(_classname);
return factory ? factory->CreateObject(_name) : Null;
}
private:
String& m_name;
};
class Dog : public Animal
{
public:
Dog(const String& _name) : Animal(_name) {};
void Talk(void) { … };
// 实现CreateObject的方法。
static Animal* DogFactoryS(const String& _name)
{
return new Dog(_name);
}
// 这里的目的是加入名为Dog的运行时类到链表。
static RuntimeFactory& GetRuntimeFactoryS(void)
{
static RuntimeFactory factory(DogFactoryS, “Dog”);
return factory;
}
};
// 实例化静态对象。
namespace { Animal::RuntimeFactory& dogfactory = Dog::GetRuntimeFactoryS(); };
class Cat : public Animal
{
public:
Cat (const String& _name) : Animal(_name) {};
void Talk(void) { … };
// 实现CreateObject的方法。
static Animal* CatFactoryS(const String& _name)
{
return new Cat(_name);
}
// 这里的目的是加入名为Cat的运行时类到链表。
static RuntimeFactory& GetRuntimeFactoryS(void)
{
static RuntimeFactory factory(CatFactoryS, “Cat”);
return factory;
}
};
// 实例化静态对象。
namespace { Animal::RuntimeFactory& catfactory = Cat::GetRuntimeFactoryS(); };
class Fish : public Animal
{
public:
Fish (const String& _name) : Animal(_name) {};
void Talk(void) { … };
// 实现CreateObject的方法。
static Animal* Fish FactoryS(const String& _name)
{
return new Fish (_name);
}
// 这里的目的是加入名为Fish的运行时类到链表。
static RuntimeFactory& GetRuntimeFactoryS(void)
{
static RuntimeFactory factory(Fish FactoryS, “Fish”);
return factory;
}
};
// 实例化静态对象。
namespace { Animal::RuntimeFactory& fishfactory = Fish::GetRuntimeFactoryS(); };
Animal* cat = Animal::CreateS(“Cat”, “Y”);
Animal* fish = Animal::CreateS(“Fish”, “Z”);
上面我把GetRuntimeFactoryS都放到匿名命名空间中调用了一次,这目的是为了实例化函数中的静态对象。为什么不直接定义一个静态数据成员呢?我太累了,留给大家自己试验吧。