据说,C#和Java有根据类名(字符串)直接创建类对象的语法,而C++没有。C++的解决方案是什么呢?当然可以通过switch-case或if-else分支实现,看过《重构》的同学都知道,这是“坏味道”的代码。
MFC中有个“动态创建”的技术,一般是按这个思路去实现,只不过不会使用宏。思路是这样的:先登记,即将类名与创建对象的方法建立一对一的关系,通常是保存在一个map中,key是类名,value是创建对象的方法。
看一个C++的例子,参考这篇文章:
//test.h
#pragma once
#include <iostream>
class Test
{
public:
Test()
{
std::cout << "call Test Constructor fun" << std::endl;
}
~Test()
{
std::cout << "call Test Destructor fun" << std::endl;
}
void print()
{
std::cout << "call Test print fun" << std::endl;
}
};
void* create_Test()
{
Test* t = new Test;
return (t == NULL) ? NULL : t;
}
//class_factory.h
#pragma once
#include <iostream>
#include <string>
#include <map>
typedef void* (*create_fun)();
class ClassFactory
{
private:
ClassFactory() {};
std::map<std::string, create_fun> my_map;
public:
~ClassFactory() {};
//根据类名创建对象
void* getClassByName(std::string name)
{
std::map<std::string, create_fun>::iterator it = my_map.find(name);
if (it == my_map.end())
{
return NULL;//没有找到类名
}
create_fun fun = it->second;
if (!fun)
{
return NULL;
}
return fun();//执行函数并返回函数执行的结果
}
//登记类名与函数指针的对应关系
void registClass(std::string name, create_fun fun)
{
my_map[name] = fun;
}
//单例
static ClassFactory& getInstance()
{
static ClassFactory fac;
return fac;
}
};
//main.cpp
#include "test.h"
#include "class_factory.h"
int main()
{
//登记
ClassFactory::getInstance().registClass("Test", create_Test);
//获取类对象
Test* t = (Test*)ClassFactory::getInstance().getClassByName("Test");
if (!t)
{
std::cout << "get instance Test err;" << std::endl;
return 1;
}
t->print();
delete t;
return 0;
}
由上面的代码,再仔细看一下注册的value是什么?是create_Test,执行创建对象的函数。其实创建对象需要“构造函数”就可以了,用JavaScript改写一下上面的代码,如下:
// test.js
const { MClass_factory } = require('./class_factory');
class MTest
{
constructor() {
console.log("call MTest Constructor fun");
}
print() {
console.log("call MTest print fun");
}
}
MClass_factory.getInstance().registClass("MTest", MTest);
module.exports.MTest = MTest;
// class_factory.js
class MClass_factory {
constructor() {
this.classReg = [];
}
getClassByName(constructorName) {
for (let item of this.classReg) {
if (item['name'] === constructorName) {
let constructorFun = item['createFun'];
let obj = new constructorFun;
return obj;
}
}
return;
}
registClass(name, createFun) {
this.classReg.push({ 'name': name, 'createFun': createFun });
}
static getInstance() {
if (!this.instance) {
this.instance = new MClass_factory();
}
return this.instance;
}
}
module.exports.MClass_factory = MClass_factory;
// main.js
const { MClass_factory } = require('./class_factory');
const { MTest } = require('./test');
let objTest = MClass_factory.getInstance().getClassByName('MTest');
objTest.print();