按照类型名称动态创建对象

LOVE杀阡陌一生

LOVE杀阡陌一生

2016-01-29 12:22

按照类型名称动态创建对象,按照类型名称动态创建对象
按照类型名称动态创建对象
作者:死猫

提交者:eastvc 发布日期:2003-9-20 10:22:27
原文出处:http://www.cpphelp.net/issue/classbyname.html


1 引言

我的一个实际项目中,由于希望通过一致的接口控制各种型号的设备,并且可以方便的随时扩充,以便将来支持更多的型号。因此,必须在运行时指定设备的型号。

为了使应用程序可以透明的控制各种型号的设备,所以建立了一个简单的继承体系,设计一个协议类(Protocol Class)作为设备的控制接口,并且为每个型号的设备设计了一个具体的类,从协议类派生并且实现了抽象的公共接口。

因此,我需要一种手段,根据设备的型号在运行时动态的创建设备类实例。否则,如果在编译时硬编码(Hard Code)设备配置,将失去实用性和灵活性。

最终的结果是,需要这样一种技术,可以实现

   Motor* motor=ClassByName("IM9001");

类似的功能。

2 设计和实现

现有的关键类的代码片断如下:

class IntelligentMotor   {   public:   IntelligentMotor(const std::string& port_name);   virtual bool Start()=0;   virtual bool Stop()=0;   virtual ~IntelligentMotor();   };class IM9001:public IntelligentMotor   {   public:   IM9001(const std::string& port_name);   virtual bool Start();   virtual bool Stop();   virtual ~IM9001();   private:   // ...   };class IM9002:public IntelligentMotor   {   public:   IM9002(const std::string& port_name);   virtual bool Start();   virtual bool Stop();   virtual ~IM9002();   private:   // ...   };// more model ...

如何实现ClassByName呢?

我们当然可以在ClassByName中使用一个多重分支检查来实现,也就是

IntelligentMotor* ClassByName(const std::string& model_name,   const std::string& port_name)   {   if (model_name=="IM9001")   return new IM9001(port_name);   else if (model_name=="IM9002")   return new IM9002(port_name);   // 所有支持的型号   else   throw "不支持该型号的设备。";   }

然而这种编程风格是糟糕的。随着系统支持的型号种类增加,分支检查语句的长度也会同时增加,造成代码尺寸膨胀和可维护性的下降。

因此必须在类型名字和类(或者类的实例)之间建立一种映射。由于派生类的指针(或引用)可以隐式的转换成基类的指针(或引用),因此很容易得到这样的构造:

struct Map  {  const std::string ModelName;  IntelligentMotor* Device;  };

进而我们还可以构造一个型号映射表,并且在编译时增加所有支持的型号。

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/cyuyanjiaocheng/)
Map ModelTable[]=  {  {"IM9001",new IM9001},  {"IM9002",new IM9002}  // 所有支持的型号  };

然后就得到了更清晰的ClassByName。

IntelligentMotot* ClassByName(const std::string& model_name,  const std::string& port_name)  {  for (int i=0;i<sizeof(ModelTable)/sizeof(Map);++i)  {  if (model_name==ModelTable[i].ModelName)  return ModelTable[i].Device;  }  throw "不支持该型号的设备。";  }

然而,在上面我故意忽略了一个问题,设备类(IM9001, IM9002等)并没有提供默认构造函数,因此实际上这些代码是无法通过编译的。可以通过修改接口的设计来避开这个问题,即增加默认构造函数和指定端口的构造函数。虽然这和实际情况不符(因为这里的设备不支持热插拔,不能再程序运行时更换连接的端口),但是为了便于实现也是可以接受的。

但是,更隐蔽的一个缺陷是,如果设备类本身的尺寸较大,那么为所有支持的型号创建实例,将增加空间负荷,而实际上实际使用的设备可能仅仅只用到其中的两三个型号而已。

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/cyuyanjiaocheng/)

从这个角度看,型号映射表中应该映射的是类的创建器,而不是类的实例本身,而类的创建器几乎没有什么空间负担。

这里有一个极其简单的创建器,

template <class T   IntelligentMotor* IntelligentMotorCreator(const std::string& port_name)  {  return new T(port_name);  }

现在我们的映射表变成了

typedef IntelligentMotor* (*Creator)(const std::string& port_name);  struct Map  {  const std::string ModelName;  Creator DeviceCreator;  };  Map model_table[]=  {  {"IM9001",&(IntelligentMotorCreator<IM9001)},  {"IM9002",&(IntelligentMotorCreator<IM9002)}  //...  };

而ClassByName则变成了

IntelligentMotor* ClassByName(const std::string& model_name,   const std::string& port_name)   {      
展开更多 50%)
分享

猜你喜欢

按照类型名称动态创建对象

C语言教程 C语言函数
按照类型名称动态创建对象

使用createElement动态创建HTML对象

Web开发
使用createElement动态创建HTML对象

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

使用反射实现根据名称动态创建窗体的几种方法

电脑网络
使用反射实现根据名称动态创建窗体的几种方法

创建高级对象

编程语言 网络编程
创建高级对象

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

创建自己的对象

编程语言 网络编程
创建自己的对象

JavaScript创建高级对象

Web开发
JavaScript创建高级对象

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

《讨鬼传 极》讨伐极级马面视频及魂铳配魂推荐

《讨鬼传 极》讨伐极级马面视频及魂铳配魂推荐

基本配色——友善

基本配色——友善
下拉加载更多内容 ↓