c++实现反射类(一)

2014-11-24 07:10:46 · 作者: · 浏览: 0
在很多程序设计中,经常会遇到这样的需求,即可以通过类的名字得到对应类型的对象,尤其是一种数据需要很多策略处理的时候。比如对于网页类型的识别,一篇网页可能是视频类型、新闻类型、图片类型、网站首页、百科等很多类型中的一种,网页类型对于搜索引擎来说是非常重要的,计算rank的时候网页类型往往是一个非常重要的因子。具体实现的时候,网页类型识别的策略可以封装在类中,这样一个策略就可以设计成一个类。但是后期随着对网页理解的越来越深入,就会出现以下两种情景:
需要添加新的网页类型,因此需要添加对应的类型识别类;
有些类型已经不再需要或者是进行了重新划分,那么需要删除掉这些类型或者是让这些类型识别模块不再生效。
  这种应用场景下,添加或移除网页类型识别模块时,最好能够非常方便,并且不会影响到已有的程序。
  一个比较好的方案是,定义一个类型识别的基类PageTypeDetector,每个类型识别策略都继承自这个基类。比如需要一个新闻页识别的新策略,那么定义类NewsPageTypeDetector,该类继承PageTypeDetector。在添加NewsPageTypeDetector到网页类型识别的主程序时,在配置文件中进行配置,添加NewsPageTypeDetector类,让该类生效,而主程序和其他类型识别策略的程序都不需要进行改动。另外,如果不再需要图片网页类型识别,那么就把图片类型识别对应的类名直接从配置发文件中删除即可。
  为了实现上述目标,我们需要从类名到类型的映射,可以称为反射。因为配置文件中的信息在程序内部得到的都是纯字符串,程序需要根据字符串生成对应的识别类。当然,这个在本身已包含反射机制的程序设计语言中很容易实现,比如JAVA,但是由于C++中语言本身不支持这种机制,因此,需要用其他的方法来模拟这种机制。
  首先,我们从最简单的方式开始,定义一个工厂方法,该方法负责根据类名生成相应类的对象,函数定义可以如下
PageTypeDetector* DetectorFactoryCreate(const string& class_name);
  生成新闻网页类型识别的类可以如下调用:
PageTypeDetector* news_page_detector = DetectorFactoryCreate("NewsPageTypeDetector");
  DetectorFactoryCreate工厂方法中的实现逻辑大致是这样:
if (class_name == "NewsDocTypeDetector") {
  return new NewsDocTypeDetector;
} else if (class_name == "...") {
   return new ...;
}
  使用如上工厂方法创建类的方式具有非常明显的缺陷,每添加或删除一个新类,都需要修改工厂方法内的程序(添加if判断或者删除if判断,并且需要添加新类的头文件或者类声明),当然了,因为程序有了修改所以就需要重新编译(如果很多其他模块依赖该程序的话,重新编译也是一笔不小的开销)。显然,这种方式虽然简单,但是极不易于维护。
  这里,提出一个使用非常方便并且易于维护的解决方案,那就是使用宏。虽然c++创始人Bjarne Stroustrup极力反对使用宏,但是在一些特定的场景中合理的使用宏会带来意想不到的效果。
  首先,从使用宏最简单的一个实现开始,目标是可以通过类的名字得到相应的对象,因此应该有个方法类似于如下:
1
Any GetInstanceByName(const string& class_name);
  返回值为Any,因为不知道返回值究竟是什么类型,所以假定可以返回任何类型,这里的Any使用的是Boost中的Any。该方法中需要new一个类型为class_name的对象返回,那么应该如何new该对象呢 借用上面使用工厂方法的经验,可以进一步使用工厂类,对于每个类,都有一个相应的工厂类ObjectFactoryClassName,由该工厂类负责生成相应的对象(为什么要使用工厂类?后面再作简单介绍)。
  有了工厂类,也需要将类名与工厂类对应起来,对应方式可以使用map object_factory_map,object_factory_map负责从类名到相应工厂类的映射,这样,就可以通过类的名字找到对应ObjectFactory,然后使用ObjectFactory生成相应的对象。但是如何将相应的工厂类添加到object_factory_map中去呢,我们需要在定义新类的时候就将对应的工厂类添加到object_factory_map中,这里需要一个函数负责添加工厂类到object_factory_map中去(为什么需要一个函数负责?最后作简单说明)。
  负责将新类对应的工厂类添加到全局变量object_factory_map的函数必须在使用object_factory_map之前执行。gcc中有一个关键字__attribute__((constructor)) ,使用该关键字声明的函数就可以在main函数之前执行。到现在,程序的结构类似这样:
// 负责实现反射的文件reflector.h:
map object_factory_map;
Any GetInstanceByName(const string& name) {
if (object_factory_map.find(name) != object_factory_map.end()) {
return object_factory_map[name]->NewInstance();
}
return NULL;
}
#define REFLECTOR(name) \
class ObjectFactory##name { \
public: \
Any NewInstance() { \
return Any(new name); \
} \
}; \
void register_factory_##name() { \
if (object_factory_map.find(#name) == object_factory_map.end()) { \
object_factory_map[#name] = new ObjectFactory##name(); \
} \
} \
__attribute__(constructor)void register_factory##name();
// 调用文件test.cc
class TestClass {
public:
void Out() {
cout << "i am TestClass" << endl;
}
};
REFLECTOR(TestClass);
// main函数
int main() {
Any instance = GetInstanceByName("TestClass");
TestClass* tes