metadata又称元数据,C++用模版在编译时玩元数据。在运行时的有,Python语言在天生上就支持元,Java的标记与反射,C#的属性等等。以至于现在的看了陈榕讲和欣OS与微软WinFS,将来操作系统的文件系统也离不开metadata。再想想Html/XML,以及COM的IDL/TLB何尝又不是metadata呢?甚至最近我在玩的Debian里Apt系列工具对安装包及其依赖的描述。这里就谈谈我所能理解的元数据。
学过面向对象编程(OOP)的开发者应该知道,我们在编程的工程中,所创造的是类(class),而类又是一个抽象的概念,类是编译器所知道的一种数据类型。我们写出类,然后编译器负责用类来规划数据与代码流程的内存模型,最终也就是映射成汇编的数据段、代码端。也就是说在程序运行的时刻是没有类和类型的,要使用一个类的功能,我们必须实例化一个类或者一个类型,产生具体的对象与变量,计算机在运行时才会分配内存给他们,编译器也才会把这个内存结构的操作链接到可运行的程序中,程序运行的时候也就是读取与操作内存中的数据,来达到目的。类与对象的关系我们已经很明确了,对象是类的实例,我们创造的是所能表示某类事物特征与行为的类,是抽象的概念。而存在的事物,就是这类事物的实际对象。
我们始终打交道的是类和对象,使用各种类的层次结构以及类的组合,可以表达复杂的逻辑,也产生了一些通用的设计模式。你会把这些模式一次又一次的使用到不同的工程、不同的类的集合中,表达这些模式相关类的代码一次又一次的被重写,甚至数据结构。除非使用统一的基类、大量使用多态,那随之而来的是效率的降低与复杂度的增加。那能不能,使类型与算法分离,甚至使用某种方法可以操作类,甚至产生类呢,然后再通过这些产生出来的类来实例化对象。
你觉得我可能要说的是模板与泛型编程,的确,这是产生式编程(GenerativeProgramming)的一种手段之一,C++中的STL库优美的使算法与类型分离,C#与Java现在也纷纷效仿,当然他们分别是在编译时、半编译半运行时、全运行时。当然这才是刚刚开始,这些都并不能操作类型,操作类型再加上泛型算法,甚至泛型模式,那么我们的编程方式就又会上升一个层次。
到这里我们就要引入新的概念,那就是元编程,把数据类型抽象成metadata,也就是说metadata是用来标识和操作类型的。想一想这样的层次,通过元数据来标识、操作、实例化类,而又不必让编译器知道确切的类,比如通过一个字符串来标识类型,并生成类,象工厂模式一样。或者,通过一个字符串访问对象的成员或方法,甚至遍历它,如Java的反射。
元数据在编程中所产生的影响,远不止如此。C++的Boost里有一个MPL库,可以用来元编程,萃取并操作类型,甚至把类型放在表里操作。在Loki里也有这样的东西,但它更关注的是设计模式范型实现,范型的设计模式代码,通过这些已有的实现,使用各种策略(Policy-Based)的组合来产生代码,从而操作类型,组合算法。现在Java中的标记与C#中属性也是要用来做类似的事情,用元信息来标记类、方法、成员,然后操作他们。在Python、 Lua这样高度动态的脚本语言来说,metadata的特性更是无与伦比,通过一个字串来产生对象,通过字串来调用方法,遍历他们的属性与方法更是不在话下。这些都带来了极大的灵活性,静态的、半静态半动态的、高度动态的,一旦类、成员、方法能够被标记,能够使其通过metadata实例化、调用、访问,通过他们即可完成更高的开发效率、运行效率或更低的复杂度。想想读入一个序列化对象所要做的工作吧,想想要跨进程传递一个调用所费的周折吧。
不止如此,有了metadata的支持,我们可以用来实现AOP、IOC这样的控制结构,拦截代码并注入代码,反转控制对象的生命周期。更甚至,使用模型来产生代码,如MDA,从模型映射到metadata,然后通过metadata映射到类代码,最后执行时使用产生的类来实例化对象,操作内存。
前面提到陈榕讲和欣OS与微软Longhorn上的WinFS,用metadata来标识文件类型?文件是磁盘上或内存里的一个数据集合,而文件又会有文件类型,现在我们使用计算机阅读文档的时候总要知道它是什么类型,用什么程序打开,去操作这个文件类型。这就好比类与对象的关系,文件是对象,文件类型是类,而metadata可以描述类,又怎么不能标识文件类型呢?我们标识了metadata文件类型的文件,还用我们去管它使什么文件类型么? metadata的实例就是文件类型、文件类型的信息及打开方式的数据,多么高明的想法啊。
想象一下,流行的XML不也是标记了数据的含义,蹩脚的COM的多语言支持也不是标记了接口么?当然都还没有metadata的高度。想到这些,更抽象的也许还在后头,这一切又将是个开始。