[笔记] 模拟java Class.forName
反射
从类型系统的角度来看,当需要在两个不同的系统中传递信息(数据)时,接收方需要知道发送方发送的数据的内部构造(数据成员,方法等),接收方在在回过头"找自己的类型"的这个过程,可以理解为反射。类似地,也可以在发送方这边完成接收方"找到类型"的这一动作,这也需要发送方能知道自身对象的内部构造。
比如实际例子中的java和sql,以及java和json等。
个人从“类型”的角度来表达一下我理解的反射。
某个系统A是基于对象的,对象里的字段都是有明确类型的,可以是int,double,或者某个对象的reference。
系统A的数据传递到了系统B。但是系统B可能是函数式的,数据的表现方式只是一个列表,而不是对象,数据的类型也不知道了。
数据类型不知道是不行的,这个列表要把自己找回来,它回过头“找寻自己的类型”的过程,就是反射。
实际的例子是,A == Java, B == SQL。系统间的分界好像水面,数据看着自己反射出的倒影发现:“哦,原来我是长这个样子的!
模拟java Class.forName
Java 有一个非常突出的动态关联机制:反射,在 Java 的情况下意味着类,我们可以在运行时加载、检测和使用,在编译时完全未知。换句话说,Java 程序可以加载一个名称只有运行时知道的类,了解其完整构造(但不了解其方法定义),并生成其对象实体,或设置其字段值,或调用其方法。
String className = "MyClassA";
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();
首先,很容易想到使用简单的工厂模式来实现。
struct Object {
virtual std::string ToString() { return "MyClass"; }
virtual ~Object() {}
};
struct A : public Object {
virtual std::string ToString() override { return "A"; }
};
struct B : public Object {
virtual std::string ToString() override { return "B"; }
};
Object *FactoryCreate(const std::string& className) {
if (className == "A") { return new A; }
else if (className == "B") { return new B; }
else { return nullptr; }
}
然后我们这样使用它:
int main() {
auto ptr1 = FactoryCreate("A");
auto ptr2 = FactoryCreate("B");
std::cout << ptr1->ToString() << '\n';
std::cout << ptr2->ToString() << '\n';
delete ptr1;
delete ptr2;
return 0;
}
看起来我们好像解决了问题,但是当我们需要创建一个新的类或修改某一个类,那么我们的FactoryCreate就不得不修改了。比如我们新增一个C类继承子Object,或者增加了一个新的基类Object2以及其派生类A’和B’,直接使用template function是不可行的,因为如果我们有新的基类,那么还需要再添加对应的if/else代码。
使用工厂模式+回调
在上述方法中,上下文是:
- factory中需要存在一个映射,字符串和创建对应类的方法
- factory提供一个接口,我们输入字符串,返回一个指向对应类的指针
- 在我们的自定义类中,如果支持reflection,应当支持自动在factory map中注册它的名字和方法(创建类的函数)
#include <iostream>
#include <string>
#include <map>
#include <cassert>
struct ClassInfo;
struct Object
{
Object() {}
~Object() {}
virtual std::string ToString()
{
return "Object";
}
static void Register(ClassInfo *ci);
static Object *CreateObject(const std::string &name);
};
struct ClassInfo
{
using ObjectConstructorFn = Object *(*)(void);
ClassInfo(std::string name, ObjectConstructorFn ctor) : m_className(std::move(name)), m_objectConstructor(ctor)
{
Object::Register(this);
}
virtual ~ClassInfo() {}
Object *CreateObject() const
{
return m_objectConstructor ? m_objectConstructor() : nullptr;
}
std::string m_className;
ObjectConstructorFn m_objectConstructor;
};
std::map<std::string, ClassInfo *> classInfoMap;
void Object::Register(ClassInfo *ci)
{
assert(ci != nullptr);
if (classInfoMap.find(ci->m_className) == classInfoMap.end())
{
classInfoMap.insert({ci->m_className, ci});
}
}
Object *Object::CreateObject(const std::string &name)
{
if (auto iter = classInfoMap.find(name); iter != classInfoMap.end())
{
return iter->second->m_objectConstructor();
}
return nullptr;
}
struct A : public Object
{
static Object *CreateObject()
{
return new A;
}
virtual std::string ToString() override
{
return "A";
}
protected:
// 方法可重载, 因此这里的注册并不完美, 且每个继承类型都需要加上改行代码来注册
inline static ClassInfo my_classInfo{std::string{"A"}, A::CreateObject};
};
struct B : public Object
{
static Object *CreateObject()
{
return new B;
}
virtual std::string ToString() override
{
return "B";
}
protected:
// 方法可重载, 因此这里的注册并不完美, 且每个继承类型都需要加上改行代码来注册
inline static ClassInfo my_classInfo{std::string{"B"}, B::CreateObject};
};
int main()
{
auto ptr1 = Object::CreateObject(std::string{"A"});
assert(ptr1 != nullptr);
std::cout << ptr1->ToString() << '\n';
auto ptr2 = Object::CreateObject(std::string{"B"});
assert(ptr2 != nullptr);
std::cout << ptr2->ToString() << '\n';
delete ptr1;
delete ptr2;
}