
RGen:Ruby建模和代码生成的框架.doc
24页RGen:Ruby 建模和代码生成的框架简介本文介绍了支持以“Ruby 方式”进行建模和代码生成的 RGen 框架[1]从 MDA 和MDD[2](但是除开那些不严格遵守这些方法的行为)的意义上说,我使用了“建模”这个名词:模型是元模型的实例元模型即是(或者是很大程度上接近于)领域特定语言(DSL)模型转换被用来将模型转换成不同元模型的实例,代 码生成是一种将模型转换成文本输出的特殊转换方法RGen 受到了 openArchitectureWare(oAW)[3]这个有着相似应用范围的 Java 框架的影响RGen 的核心思想不仅仅是使用 Ruby 在框架内作为应用程序的实现逻辑,而且还用来定义元模型,模型转换和代码生成 RGen 通过对每个切面提供内在的DSLs,简化了这个过程其他的项目也证明了 Ruby 十分适合在在这种情况下使用一个著名的案例即是 Ruby on Rails[4],它包含了一些内置的 Ruby DSLs但是,oAW 使用了一些外部的 DSLs 来定义模型转换和代码生成经验告诉我们 RGen 方法是非常的轻量级,而且极其灵活,使得开发更加高效,部署更加简易我发现在那些启发式的项目中,即 发现缺少支持的工具,却没有 预先制定工具开发计划的项目中特别有用。
使用 Ruby 和 RGen 我们能够以最小的努力来开发需要的工具,而且人们将会从这个工具中受益非凡使用诸如 RGen 和 oAW 这样框架的典型应用是代码生成器(例如在嵌入式设备中)和构建以及操纵模型的工具,通常以 XML 或者一种自由的文本或者图形语言表示在本文中,我将会使用“C++代码生成器的 UML 状态图”作为例子在现实世界中,我们仍然在上述的项目中使用 RGen 来为汽车的嵌入式电子控制单元(ECU)进行建模和生成代码的工作模型和元模型建模框架最重要的基本方面是表示模型和元模型的能力元模型描述了特殊目的的模型看起来会是什么样,即定义了领域特定语言的抽象语法典型地说,一个建模框架与元模型的应用和这两者之间的相互转换相关RGen 在 Ruby 中采用了一种前向的模型和元模型表示方法,就像面向对象的语言一样:对象用来表示模型元素,类用来表示元模型元素模型和元模型之间的关系以及它们的 Ruby 表示法如图 1 所示为了支持领域特定语言,一个建模框架必须支持自定义的元模型RGen 通过提供元模型定义语言,简化了这个过程, 这个语言看起来和领域特定语言很相似像其他的 DSL 一样,元模型定义语言的抽象语法也是由其元模型来定义,也就是所 谓的元-元模型。
图 1:模型,元模型以及其 Ruby 表示不像元模型,元-元模型在 RGen 里面已经得到了修复此架构使用了 ECore -- Eclipse 建模框架(EMF )[5]的元-元模型 图 2 是 ECore 元模型的一个简单视图:在ECore 中,元模型基本由一些以层级的包组织起来的类组成,这些类的属性和引用相互指向从多个超类中可以抽象出一个类来引用是无向的,但是可能会和一个相反的引用连接在一起,因此成为有向的引用引用的目标便是其类型,必 须是一个类;目标的角色便是引用的名字属性是(非类的)数据类型的实力,可能是原生类型或者是枚举类型图 2:ECore 元模型的简化视图和其他例如 oAW 的框架相反,RGen 的元模型定义语言的具体语法是 Ruby 形式,一种内部的 DSL列表 1 描述了简单的状态机元模型:代码使用普通的 Ruby 关键词来定义一个模块 1 和一些类分别表示一个元模型包和元模型类为了从普通的Ruby 类和模块中分辨出这些元素,需要一些 额外的代码:模块加上了一些特殊的RGen 模块扩展(1),而且这些类都是继承于 RGen 元模型的基类 MMBase(2)元模型类的超类关系由 Ruby 类继承来表示(3)。
注意到 Ruby 原始不支持多重继承,但是得益于其灵活性,一个特殊的 RGen 命令可以支持这个特性这样,这个类必须是 MMMultiple(,,……)的返回值, 这个方法是一个全局方法,用于在全局中构建一个中间超类 表 1:元模型的状态机样例module StatemachineMetamodelextend RGen::MetamodelBuilder::ModuleExtension # (1) class ModelElement 'TargetState1') # (4)t1 = Transition.news1.addOutgoingTransitions(t1) # (5)assert_equal s1, t1.sourceStatet1.targetState = s2 # (6)assert_equal [t1], s2.incomingTransitionss3 = State.new(:name => 'TargetState2')t2 = Transition.new(:sourceState => s1, :targetState => s3) # (7)assert_equal [s2,s3], s1.outgoingTransitions.targetState如上所示,RGen 元模型定义语言创建了需要表示 Ruby 元模型的模块、类和方法。
不仅如此,元模型本身还可以作为一个普通的 RGen 模型因为 RGen 包含了ECore 的元模型,ECore 的元模型用其自身的元模型定义语言来表达元模型的RGen 模型能够通过表示元模型元素的 Ruby 类或者模块中的 Ecore 方法来存取列表 3 的例子是:在 StatemachineMetamodel 模块上调用 ecore 方法得到了EPackage 的一个实例(1),在 State 类上调用会得到一个 EClass 的实力(2)而且这两个都是属于同一个模型,事实上名字叫做“State”的 EClass 是叫做 “StatemachineMetamodel”的 EPackage 中的一个分类器(3)元模型 Rgen 模型能够像其他的任何 RGen 模型那样被操作样例代码说明了“State”类的超类有一个叫做“name”的属性(4) 表 3:存取 ECore元模型smPackage = StatemachineMetamodel.ecoreassert smPackage.is_a?(ECore::EPackage) # (1)assert_equal 'StatemachineMetamodel', smPackage.name stateClass = StatemachineMetamodel::State.ecoreassert stateClass.is_a?(ECore::EClass) # (2)assert_equal 'State', stateClass.nameassert smPackage.eClassifiers.include?(stateClass) # (3)assert stateClass.eSuperClasses.first.eAttributes.name.include?('name') # (4)作为一个普通的模型,元模型模型能够被任何可用的序列器和初始器序列化和初始化。
RGen 包含了一个 XMI 序列器以及 XMI 初始器,使得元模型能够和 EMF 进行交换同样地,元模型模型能够作为 RGen 模型转换的源或者目标,例如从或者到一个 UML 类模型模型转换将会在下节中讲到最后,使用 RGen 元模型生成器,元模型模型能够返回到 RGen 元模型 DSL 表示图 3 总结了不同的元模型表示方法以及它们之间的关系图 3:RGen 元模型表示总结RGen 元模型模型提供了类似于 EMF 的在元模型上的反射机制反射机制对于程序员来说是非常有用的,例如,当实现一个自定义的模型序列器或者初始器的时候事实上元-元模型是 ECore 用来确保大量的建模架构的可交换性:使用 RGen 元模型生成器,任何 ECore 元模型能够直接在 RGen 中使用表 4 表示了元模型模型到 XML(1)的序列化过程,和使用元模型生成器来重新生产 RGen DSL 表示法(2)一样注意在这两个例子中,元模型是被StatemachineMetamodel 的 ecore 方法返回到根 EPackage 元素所引用生成的元模型的 DSL 表示法可以从文件中读取和解释(3)为了避免在初始的类、模 块和重载的类、模块之间的名字冲突,解释是在名字空间中进行。
如果不考 虑在重载版本中的“instanceClassName”属性值,那么这两个模型是一样的 列表 4:序列化元模型File.open("StatemachineMetamodel.ecore","w") do |f|ser = RGen::Serializer::XMI20Serializer.newser.serialize(StatemachineMetamodel.ecore) # (1)f.write(ser.result)endinclude MMGen::MetamodelGeneratoroutfile = "StatemachineModel_regenerated.rb"generateMetamodel(StatemachineMetamodel.ecore, outfile) # (2)module RegeneratedInside = bindingendFile.open(outfile) do |f|eval(f.read, Regenerated::Inside) # (3)endinclude RGen::ModelComparatorassert modelEqual?( StatemachineMetamodel.ecore, # (4)Regenerated::StatemachineMetamodel.ecore, ["instanceClassName"])现在 RGen 提供了对运行时元模型动态改变的有限支持。
尤其是,ECore 元模型模型的改变不会影响内存中的 Ruby 元模型类和模块但是,现在我们仍然致力于实现 Ruby 元模型表示的动态版本 动态元模型包含了动态类和模块,紧密地和EClass 还有 EPackage ECore 元素联系在一起当 ECore 元素被修改的时候, 动态类和模块将会立刻改变它们的行为,即使实例已经存在这个特性能够支持下节将要讲到的模型转换的高级技术RGen 最大的一个优势便是使用内部 DSLs 以及和 Ruby 紧密关联所获得的好处这样使得程序员能够程式化地创建元模型类和模块,然后调用类的方法来创建属性和引用利用这个优势的一个应用程序便是一种 XML 初始器,它在运行 时根据遇到的 XML 标签和属性,以及一系列的映射 规则,动态地创建目标元模型RGen分发版包含了这个初始器的原型版另外一个有意思的是使用内部 DSL,可以将元模型嵌入到常规代码中 这个特性是非常用帮助的,因为代码有的时候必须要处理复杂的,内部有联系的数据结构 开发者也许会考虑(元模型)类,属性和引用的结构,然后在定义域内使用它们使用这种元模型方法,开发者能够决定在运行时 5 自动地检查属性和引用。
模型转换许多现实世界的建模应用能够从一些元模型的使用中获益。
