MOF原理和实现
陈健翔
2004-10-15
众所周知 MOF 是 MDA 的核心,下面我们来快速浏览一下什么是 MOF 。
序曲
一个应用系统可以被分为典型的 3 层结构。在《 UML 模式与应用》 [1] 一书中分别称为 UI 层、领域层、数据层。 UI 层和领域层的接口通过应用程序和应用框架来连接;领域层和数据层的接口通过持久化框架来连接。该书中关于持久化框架的实现是使用模板方法模式来实现的,基类定义了持久化基本接口,由每个子类来实现各自的持久化方法。子类的域( field )和关系数据库的字段的映射关系被直接编码到类代码中。该方案会导致系统复杂性的提高和系统维护的困难。为了解决该问题,该书引用另一本书的一种解决方案,即 POSA [2] ( Pattern-Oriented Software Architecture: A System of Patterns )的反射模式。
POSA1 中的 Reflection 一节讲述上述的反射模式,或称为映像 [3] 模式。映像模式将软件系统(领域层)分为基本层次和元层次。基本层实现程序的逻辑,元层次封装可更改的系统内部成分。元层次由一组元对象( metaobjects )组成,每个元对象封装了一个关于基本层次的结构、行为或状态的一方面的所选信息,所有元对象一起提供一个应用程序的自表示。将持久化方法封装到元层次后,基本层次通过调用元层次提供的持久化组件来序列化和反序列化内部数据。同时可以通过元对象协议( MOP )改变元层次的持久化方法,以影响整个系统的持久化行为。该模式示意图如下:
将元层次从系统中独立出来后,对元层次的访问变得十分重要了,这就直接导致了元层次的互操作性和兼容性问题,即不同模型系统表达的元层次能够互相访问、接口兼容。我们来看映射模式中元层次信息的结构:元层次保存的信息包括类型信息( type_info ),存放在元层次类型库中;扩展信息( extTypeInfo ),提供类大小、继承关系和数据成员信息等,对象与关系数据库的关系可以记录在扩展信息中。由于建模语言的不同,定义出来的元数据虽然内容相似,但是格式、接口却不同。为了解决这个问题,就需要一个统一的格式和接口来描述和访问元数据,这就是 MOF 要做的。
MOF 体系结构
我们知道,为了实现跨语言和跨平台的对象访问, OMG 提出了 CORBA 体系结构,并定义了 IDL 用于统一对象访问接口。 MOF 接口是 OMG 提出的在元层次上定义的统一的接口。和 OMG 一贯思想一样,它也是跨语言和跨平台可访问的。另外,注意到跨平台访问和跨平台运行是不同的两个概念, Sun 的 Java 是跨平台运行的,但 Eclipse 就不一定了; C++ 程序通过 CORBA 能够做到跨平台访问,但是做到垮平台运行却比较难。
MOF 体系结构是分层的元数据体系结构,该体系结构的经典框架有 4 个建模层次的,如下图所示:
每个层次的上一层是下一层的的模型,本层次的描述语言在它的上一层模型中。比如元层次股票报价类 StockQuote 用于描述软件系统中的报价对象;而元 - 元层的类 Class 则是用于描述模型层,如元层次类、包等的建模元素;同样,元 - 元 - 元层,简称为 MOF Model 层,该层的 Class 则是用于描述元 - 元层,也称为元模型层的中我们通常说的类,包,关联这些建模型元素;那么最后 MOF Model 由谁来描述呢?答案是 MOF Model 自己:“上层” MOF Model 中的 Class 可以描述下层 MOF Model 的 Class , Data Type , Association , Reference , Attribute , Operation 等等这些 Model Element 类的派生类,包括 Model Element 类在内;“上层” MOF Model 中的 Package 可以描述下层 MOF Model 的 Model 、 Reflective 、 PrimitiveTypes 、 CorbaIdlTypes 等这些包;“上层” MOF Model 中的 Association 可以描述下层 MOF Model 的包括泛化在内的所有关联 ... 更简单地说吧,就是: MOF Model 包含了一个名为“ Model ”的 Package ,这个 Package 中有很多 Class ,这些 Class 使用一种名为“ Generalizes ”的 Association 关联成一棵树,根节点是“ Model Element ”这个 Class 。
元层次和基本层次之间的关系是相对的,和元层次对基本层次之间的关系相似:元 - 元层次也起到封装元层次内部可变成分的功能。以此来看,在 4 层结构中, MOF Model 是 metamodel 的元层次,包括了 metamodel 的元对象和对应的 MOF 接口,提供给特定的功能模块访问,该功能模块就像前文提到的持久化组件一样。模型层和元模型层也是相对的,任何一个模型层,包括元模型层,的上一层都称为本层次的 metamodel ,而 MOF Model 是它自己的 metamodel 。 MOF metamodels 则特指提供了 MOF 接口的元 - 元层。
而实际上, MOF 元数据体系结构的元层结构不是固定的。虽然有典型的 4 层,其实是可多可少的,取决于 MOF 如何部署。在上面的 4 层结构中 MOF 部署在 metamodel 的上一层,这纯粹是为了方便于理解数据和元数据之间的关系。 MOF 可以用于描述任何一个模型层,为他们定义一致的接口。如: MOF 用于定义 model 的统一接口,使客户程序可以通过 MOF 接口访问 model ; MOF 也用于定义 metamodel 的统一接口,使客户程序可以通过 MOF 接口访问 metamodel ;其实当在 model 层使用 MOF 的反射接口时,就会获得 metamodel 的 MOF 接口。即 MOF 能够为每个元层提供接口。在下文谈到的 MOF 映射中,就是以 MOF 部署在 metamodel 层描述 model 层为例的。明白这一点,就明白了 MOF 是什么了:为任何模型定义统一接口。“任何模型”可以是信息模型、元模型、 MOF 模型自身。如下图:
MOF 提供了一套 CORBA 接口,使用 CORBA IDL 来定义,以此为基础统一了不同模型间的接口,使能够兼容和互访。之所以使用 CORBA IDL ,是因为对象访问是一种更一般的访问方法,而 CORBA 提供了对象访问的平台; MOF 虽然是对模型的建模,但 metaobject 也是对像, MOF 终究还是面向对象的;实际上,不但 MOF 是面向对象的,各个元层包括信息层( information )都是面向对象的;所以都可以使用 IDL 来实现无阻隔的对象访问。
由于 MOF 有三个元层次;并且在叙述不同层次时在词汇上共享了意思相近或相同的名词,有些名词仅在版上做了区分,比如 MOF Model 中的建模构造( Construct [4] ) Class 和通常说的或者 UML 中的 class 的区别仅仅是首字母大写;同时 MOF 使用 UML 来描述,而 UML 的图元又不是 MOF Model 的建模元素,比如泛化在 MOF 中用关联来描述;所以刚开始时,它的规范和概念看起来容易迷惑。不过随着对规范的熟悉,就知道一些规则,如:首字母大写的名词如 Class 、 Package 、 Association 等表示的是上一层的构造元; MOF metamodel 表示支持 MOF 接口的 metamodel 层的模型。顺序细读规范也会注意到它对一些术语的使用做了说明的,规范的前后文关系还是比较密切的。
插曲
MOF 体系结构看起来很美,似乎超越了所有建模语言,是一个完美的形式系统。从集合角度看, MOF Model Package 是这样一个集合:它是集合的集合,即它的元素也是集合,如 Class , Package , Association 。从它的元素中取出“子”元素来能够组成这样一个新的集合,这个新的集合居然还是 MOF 。就像魔术师一样,把一张报纸撕碎了并且烧掉,然后从残留的灰烬中取出那么一小点,“噗”一下又重新变出整张报纸。
后来才发现类似 MOF 这样形式语言的研究在其他领域很早就有了,在网上搜索“形式系统”、“元数学”,就会搜到:希尔伯特 .D ( Hilbert , David , 1862 ~ 1943 )在 1920 年代创立了元数学。更一步想:要是有一天形式语言发展到人的语言和计算机语言能够互相访问,那岂不是人说一句话就能产生一个可运行的程序,就像电影《 The Matrix 》中描写的: Neo 和 Trinity 进入 Matrix 打算营救 Morpheus ,他俩 站在系统中, Neo 说了一句:“ Guns, lots of guns... ”,许许多多排的枪械“唰唰唰”的飞奔而来 ... 胡思乱想,扯远了。不过最近日本人发明了一种语言翻译系统,据说不错,好像不是像以前比较原始的“东方快车”那样逐字翻译的,不知道它和 MOF 原理是否一样:先把一种语言的一段文字解释、建模,然后根据另一种语言的形式模型映射成另一种文字。关于映射我们来看下面。
MOF 映射
MOF 映射是在给定实现技术的环境中将 MOF 的语义用另一种实际的技术再实现的过程。一个客户程序通过 MOF 可以读出一个模型(比如 metadata 层的 model )中有哪些 Class 实例,哪些 Package 实例等等,当把这些元素读出来以后,就要实现为 Java 模型中的 Class 实例和 Package 实例,或者 UML 模型中 Class 和 Package 实例,或者 IDL 中的 Interface 和 Module 实例。通过映射能将 MOF 描述的模型用具体技术重建出来。
映射中最重要的是抽象映射( Abstract Mapping ),它描述如何将 MOF metamodel 充实为一个抽象的信息模型( information model );即如何使用 metamodel 的构造拼写出 metadata 的逻辑结构。抽象映射给出了映射的语义和规则,是所有 MOF 具体映射的参照和准绳;任何具体映射如果有违抽象映射规则,那么将和其它模型不具完全的互操作性,比如和 CORBA IDL 映射的模型之间。即不合法映射产生的模型和正确映射产生的模型之间不能完全互操作。
举一个例子能够比较形象的说明 MOF 映射问题。在 MOF 规范中有下面这样的例子。这个例子是 MOF 到 IDL 的映射,是规范中的图 5-1 。关于该图需要说明的是:规范中又将 information 、 model 、 metamodel 、 MOF Model 各层分别称为 M0 、 M1 、 M2 、 M3 层。
图的最左边是对 M1 层模型结构进行描述的 M2 层: Package 是 M2 层的构造( construct ); P 是 Package 在 M1 层的一个实例;类似的 C 和 A 分别是 M1 层 Class 实例和一个 Association 集合 [5] , C 通过 A 产生自关联。如果是 Java 模型的话, A 中的元素即一个链接( link )可以用 Attribute 实现,而 Attribute 是有类型的,所以又关联回 C ,我们可以画一个展开图来看:
所以左图的意思简单的说是: M1 层有包,包中有类,类通过关联连接起来。为了实现这个概念模型,通过最右边的方法来进行:一个包工厂创建一个包实例 P ,包实例中有一个类代理,类代理创建多个类实例,类实例之间的关联全部存储在关联的实例集合 A 中。直接一点理解可以把 P 对应 Java 中的 Package , C 对应 Class 。图的中间部分是概念模型到映射结果的五种接口,通过它们访问具体的 IDL 映射模型。而这五种接口也正是 MOF Model 的 Interface ! [6] 这五种接口的每种接口可能不止一个接口实例。比如 C 这种接口,每个在 M1 层的 C 实例分别对应一个接口,如可能是 IClass1 , IClass2 这样的多个接口实例; P 也是一样,可能包括 IPackage1 , IPackage2 , IPackage3 等等这样多个接口实例。
模型互访
通过映射将 MOF 和具体建模技术结合起来了,下面我们来看看 MOF 实现结构。图中每个模型( Model1 和 Model2 )都提供了 MOF 接口,客户程序(包括访问其它 model 的 model ,如图中 Model1 )通过 MOF 看到的是 Model1 和 Model2 的 MOF 模型,而不再是具体技术(如 UML )构建的特殊模型了。至此可以看出 MOF 设计思路其实和 CORBA 是很相似的。
有了这样的平台,通过 MOF 模型与模型之间就可以建立大使级外交关系了。不但是 MOF 的 model 层与 model 层之间, metamodel 层和 metamodel 层之间,还可以是 model 层和 metamodel 层之间 ... 只要遵循 MOF 规范就能互访。同时由于 MOF 是建立在 CORBA 之上,所以它还做到了跨语言和跨平台。补充说明一下:一般而言分层系统是不允许交叉访问的,每个层只能建立在它下面的层之上,但像 MOF 这样描述性分层系统,层与层之间是允许双向依赖的 [7] 。
尾声
回到本文开始。我们开始说的持久化框架用于领域模型,其实 GUI 模型也是可以持久化的,就像许多开发系统所做的那样,开发人员定义的用户界面及其界面相关数据,往往需要被持久化,如保存在某个特定格式的文件中或保存到数据库中。当程序编译时这些数据再被静态连接到可执行文件中;或程序运行时再去读数据然后加载到系统中。就像领域层设计的方法一样,我们可以使用映像模式来实现它。当然是在如果有需要这么做的情况下,因为映像模式还是有它的缺点的。
花絮 1
应用系统使用到元 - 元层的可能比较少被人所知,下面对元元层的功能做一补充描述,该描述属于推测、猜想类,并不是从已知应用引用而来。我们来看个例子:比如在 Java 环境中,元 - 元层可以将方法( Method )调用( invoke )的信息存储到一个“元 - 元对象”中,当一个代理类调用该方法的 invoke() 时,可以通过元 - 元层的组件 Invoker 来实现 invoke() 。 Invoker 通过读取元 - 元数据来执行操作调用的行为。比如元 - 元层可能记录了方法调用时需要记录日志,则 Invoker 将会把每个调用记录到日志中;元元层也可能记录了方法调用时通过特定的通信协议,那么 Invoker 将使用该协议执行本次调用。由此得知,元 - 元层可以影响到整个模型和信息系统的行为。上述内容如下图所示。图中, StockQuote 是一个领域层的类; ClassProxy 实现了 java.lang.reflect.InvocationHandler 接口。本图只是示意图,不是标准的 UML 图。
花絮 2
MOF 规范的 1.2 节 Software Development Scenarios 讲述了一个案例:用 MOF 定义一种新的建模语言,称为宇宙设计语言( UDL: Universal Design Language )。围绕该语言,构建了一个软件开发系统。该系统建立起来的软件模型通过 MOF 实现了互操作性 ... 具体请参见规范原文。该案例对我们根据具体领域和业务构造一个适合的建模语言和方法有一定参考价值。
[1] 该书第一版。该书第二版做了修订,改进了原方法,引入 object-relational (O-R) mapping 的概念。
[2] 因为写书当时还没有出 POSA2 ( Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects ),所以是 POSA 而不是 POSA1 。而 POSA2 同样精彩,而且对模式语言做了更详细的描述,值得推荐。
[3] 机械工业出版社那本书之所以将 Reflective 翻译为“映像”可能是因为类和它的反射就象镜子前的物体和镜子里的像一样,和镜子里的像相对的,类的反射称为“映像”。而反射是动词用作名词,偏重于产生像的动作本身,所以用名词“映像”作译词。
[4] 暂译为构造,是因为它是一个表达单元,能够被实例化( instance ),实例化的过程就是构造对象的过程。“构造”动词用作名词化,或者译作构造元,构造体,构件,结构等。
[5] 为什么是一个请参见 MOF 规范 5.2.1 Meta Object Type Overview
[6] 参见 MOF 规范 3 MOF Model and Interfaces 。
[7] 参见 POSA1 的 Reflection 一节。