UML软件工程组织

XDE中模式驱动的设计与开发(三)
刘武东yesky
  第三部分:XDE中模式的高级话题

  在前面的部分中,我们详细介绍了XDE的使用方法,但是XDE中关于模式的概念有很多,有一些很直接,而有一些却比较的隐讳。这一部分的内容,将对XDE中的一些高级概念作初步的阐释,并给出了一些小的例子。希望能够帮助大家更在对XDE本身,以及XDE所提倡的模式驱动的开发方法有更多,更为深入地了解。如果没有看过前几期的读者,还是最好找来看看,直接看着一期的内容,理解起来可能会有一些困难。

  1. 代码模版(Code Template)

  模式,或者说设计模式,很大程度上是对对象结构上的描述。也就是说,模式最终的实现以及生成的代码,也大多数是属于框架性质的--只有类,属性或者方法的定义,类之间的引用,继承等等之类的代码,而对于具体的方法中的代码,它往往无能为力。这也是大多数正向工程工具都欠缺的一个部分。

  实际上,如果只是一些简单的语义,比如对象的创建,固定方法的调用而没有涉及复杂的交互的,我们也是能够从模型的语义中生成具体的代码。在XDE中,通过代码模版,来完成一些简单方法的参数化的代码生成。

  如同模式一样,代码模版也是可以拥有参数并在具体生成的时候进行替换绑定的。在代码模版中,可以使用两种类型的参数:strings和model elements。

  Strings:简单的字符串,在代码生成的时候XDE会用具体的字符串来替换所有的这些字符串参数。

  Model elements:一个模型元素。XDE中提供了一个简单的编程模型,可以通过对模型元素API的调用(比如得到一个类的所有公有方法)来完成给位复杂的代码定制。和模式联系在一起的时候,Model Elements类型的参数可以是模式中的一个模版参数,在模式展开的时候,会用具体的模版参数值来替换这个代码模版中参数。

  XDE代码模版的形式很像Jsp或者Asp中使用的形式。你如果对Jsp的机制很了解的话,那么你也可以很容易的理解代码模版的机制了。

  在一个代码模版中,代码被分为两个部分,一部分是直接输出的,不经过任何的处理。另外一个部分是在<%和%>这两个标号之间的脚本内容,通过对参数或者其它元素的处理之后再进行输出。如果Jsp或Asp一样,它也是用简单的<%=var%>来进行变量的输出(var是一个变量)。所有<%和%>之间的内容,是使用脚本语言来编写的。现在XDE中的代码模版只支持Javascript语言。

  下面的一个代码模版的例子选自XDE的在线文档,用以在调试的时候打印对象的当前状态。每一个应用了这个代码模版的方法,将会在调用方法前在控制台输出对象的状态,供调试使用。

<%
// assume: myClass is "this" Class with debug operation
function debugStatements(myClass) {
var attributeCollection = myClass.GetAttributeCollection();
var attributeCollection1= Interfaces.queryInterface(attributeCollection, "com.rational.rms.IRMSElementCollection");
var attributeCount = attributeCollection.getCount();
debugStatements = "";
for (i=1; i<=attributeCount; i++) {
var rmsAttribute = attributeCollection1.GetElementAt(i);
var attrName = rmsAttribute.getName();
%>
System.out.println( "<%=attrName%>" );
<%
}
}
//assume: myOperation is debug operation
function debugOperation(myOperation) {
var thisOperation = Interfaces.queryInterface(myOperation, "com.rational.uml70.IUMLOperation");
var thisClass = thisOperation.GetContainer();
var myClass = Interfaces.queryInterface(thisClass, "com.rational.uml70.IUMLClass");
debugStatements(myClass);
}
var myOperation = Interfaces.queryInterface(thisElement, "com.rational.uml70.IUMLOperation");
debugOperation(myOperation);
// end
%>


  在上面的代码模版中,定义了两个方法debugStatements和debugOperation,debugOperation接受当前元素作为参数,并由其得到debugStatements的参数--一个包含了这个方法的对象,并在debugStatements中输出:System.out.println( "<%=attrName%>" );

  以在控制台输出对象的状态。

  在代码模版中,可以用一个"thisElement"的标准的预定义变量来代表代码模版被应用的元素,在当前版本的XDE中,只能够在类中的方法上应用代码模版。

  当然,代码模版的一个最大的作用,就是同模式的模版参数一起来使用了。一个最简单的例子,比如说,如果我创建了两个模版参数,设为tp1和tp2,分别代表了两个类。我需要在tp1代表类的方法op1()中创建tp2所代表类的一个对象,并把它赋给一个tp2类型的引用。那么我们可以为代码模版中定义一个参数codetp,类型为String,并为其赋值为<%=tp2%>。则在为op1()所创建的代码模版中,我们可以这样写:

<%=codetp%> a<%=codetp%>Object=new <%=codetp%>();

  假设tp2最后被绑定到一个名为ClassTP的类上,代码模版被展开后的结果就是:

ClassTP aClassTPObject=new ClassTPObject();

  这样就完成了我们想要的功能。

  这只是一个最为简单的功能。实际上,XDE中的代码模版的功能是非常强大的,通过javascript脚本语言和XDE内建的编程模型,我们可以创建非常复杂的代码模版,使得代码的生成率大大提高。

  2.模式小脚本(Scriptlets)

  小脚本是一种可执行的代码片断,实际上在上面对代码模版的介绍中,我们已经接触到这种小脚本。<%=var%>就是一种简单的小脚本。小脚本不仅能够应用在代码模版中,还可以使用在模型的其它地方,比如类,属性,或者方法的名字,元素的属性值,模型的文档注释,关联的端点名,等等。几乎在任何可以使用字符串的地方都可以使用小脚本。这种小脚本的语法很简单: <%=scriptlet text%>。使用javascript脚本语言,还可以在<%和%>标记之间加入其它的程序片断。

  小脚本在模式被展开的时候被运行,并用运行的结果字符串来替代这段脚本。最为普遍的一个用法是用来动态的替代模版参数的名字。比如,如果在模式中定义了一个名为tp1的模版参数,那么小脚本<%=tp1%>在模式被展开时被替换成tp1所绑定的参数值的名字。如果tp1帮定到一个类名为TPClass的类上,那么最后所有的<%=tp1%>都被替换成TPClass。

  复杂一点的,比如,我们可以在对这个类的文档中使用这个小脚本:

Name Length: <%= tp1.getName().length()%>
Name Substring: <%= tp1.getName().substring(0, tp1.getName().length()-1)%>

  这样,最后的文档也完成了。

  在脚本中使用的具体的API在Rational尚未公布,但是可以使用如下的一个小技巧来得到一个模型元素的API。首先定义一个函数

function show_props(obj, obj_name) {
var result = "";
for (var i in obj) {
EAEventData.AddOutputMessage(obj_name + "." + i + " = " + obj[i]);
}
}

  然后再调用它:

show_props(tp1, "tp1");

  这样这个脚本能够在XDE的输出窗口中输出给定模型元素可以被使用的API。

  3. 值源(Value Source)和值集

  在创建一个参数的时候,你可以选择的指定这个参数的一个值源,来指明这个参数所接受的输入的方法。这些方法有如下的三种:

  · User:缺省值。在选择了这种参数输入方法后,意味着在应用模式时,用户必须从现有的模型中选择一个类型相符的元素来作为传递给这个参数的值。

  · Generated:这个值意味着在应用模式时参数值将被自动的创建。用户只需要提供一个字符串作为生成的参数值的名字即可。

  · Collection:从一个给定的元素中派生出一个值或者值集,来为目标参数赋值。这个给定的元素,被称为Collection的宿主元素(Owning Element),它可以是任何的模型元素。在指定了宿主元素的名称后,可以为其创建过滤器,来选择需要从中派生的内容,即Collection。一个简单的例子,你可以选择一个类的所有公有的属性作为一个Collection,然后将这些属性值传递给目标参数。

  这些方法可以被单独的使用,也可以被组合使用来给用户提供多种选择的方法。

  XDE中为某些类型的模版参数提供了一种值集的机制,来限定模版参数的取值。比如可以为Integer类型的模版参数提供一个值集,在应用模式的时候,这个模版参数的取值就只能够从给定的集合中选取。有些时候这种技巧是很有用的,可以约束用户的取值范围,不会出现不合法的结果。

  4. 用约束创建可选元素

  XDE中可以通过约束来创建一个可选的元素。这个元素是否在模式展开的时候被生成,取决于给定约束的值:如果约束的值为true,它就会被生成;反之,就不会被生成。这种约束可以被应用到模型中任何的元素上。约束的条件,通常取决于某一元素属性的取值。比如,判断一个类的可见性是不是public什么。也可以通过AND,OR,NOT来连接单个的属性判断,来构造更为复杂的约束。

  例如,下面的表达式:

Model1::Package2::Class1.Visibility = "PUBLIC"

  就是一个约束,用来判断Model1::Package2::Class1的可见性(可以是PUBLIC,PRIVATE,PROTECTED或者PACKAGE)是否为PUBLIC。如果是,约束表达式返回值为true,否则,返回值为false。如果 Model1::Package2::Class1不存在,表达式指出有错误存在。

  在XDE的模式定义中可以有两种约束存在:

  1、 属性约束(Property constraints):是对模型元素的元模型属性的约束。上面的例子即为属性约束。

  2、 关系约束(Relationship constraints):用来判断模型元素之间的特定关系。例如,两个类之间的继承关系,类和接口之间的实现关系,等等。

  你可以不用去记得到底某个类型的元模型中到底有哪些属性,某两个类之间到底有哪些方法,在XDE中,有一个约束编辑器来帮助你构建约束,所有的工作只需要用鼠标选择即可。

  5. Callouts

  模式的Callouts用来响应一些事件,比如在应用模式之前,或者在应用模式之后,分别对应着PreApply和PostApply这两个Callouts。在发生这些事件的时候,相对应的Callout中定义的脚本被调用。

  并非所有的模式都需要使用到Callouts,它只是用来对付一些用一般的模式机制难以解决的问题。Callouts 能够定义在模式或者模版参数上。 如下图:



  在创建一个Callout的时候,通常需要使用到Javascript来进行一些处理。在XDE中,有一个全局的EAEventData对象,它提供对模式引擎以及XDE中其它部分的编程接口。但是,关于它的细节,因为文档不全,我们尚未得知,估计在以后的XDE版本中会有披露。一个简单的例子如下:

EAEventData.AddOutputMessage("Test message");

  在EAEventData对象的AddOutputMessage方法可以在output视图中添加一行的字符串数据。

  因为Callouts提供了对模式引擎和XDE的借口,理论上来讲,它可以来完成任何模式引擎能够完成的功能。因而它是非常强大的。但是,我们还是应当尽量的避免使用它,因为它太复杂而且具体的接口尚未被标准化。在考虑使用Callouts之前,应该先查找模式设置的其它地方,看看是否已经被实现了。

  6. 模式的组合

  很多时候模式并不是单独使用的。例如在使用抽象工厂模式(Abstract Factory)的时候,我们往往需要将抽象工厂定义为一个单件,只允许由一个抽象工厂的实例存在。这样,我们就需要使用到另外的一个创建型模式:单件模式(Singleton)。这样的模式组合的例子其实很多。XDE中一个简单的使用办法,就是先应用一次抽象工厂模式,再在模式展开的基础上应用一次单件模式。但XDE还提供了更为强大也更为复杂的方法:将模式组合在一起,创建一个新的模式--就上面的例子,我们姑且称其为工厂单件模式吧。这样,我们只用这个应用模式一次,就可以得到很好的效果。

  这样的实现在XDE中其实并不难。只需要在定义模式的结构时,在引入以定义好的模式即可。比如,在定义抽象工厂模式的时候,假设现在所有的抽象工厂的参与者和模版参数都已经建立好了。要引入单件模式,只需要简单的在上下文菜单中选择,就如同在一般情况下应用模式一样的,将抽象工厂中的参与者ConcreteFactory指定为单件模式中Singleton模版参数的参数值,然后应用单件模式即可。这种模式的组合为更大,更复杂的问题提供了解决方案。

  总结

  Rational XDE确实是一个非常强大的功能,本文对其在模式建模及应用方面的话题作了详细的讨论,基本上涉及了XDE中模式的方方面面。相信大家对XDE中基于模式开发的概念应该有了一个比较清楚地了解。而实际上,模式的功能只是XDE中的一个小的部分,还有很多其它的功能也是非常的强大的,比如正向/逆向工程。

  但是,在现阶段,XDE还只能被称为是一个很有潜力的产品,尚不能称为一个成熟的产品。一方面,它所提倡的开发模式和开发方法还不是非常的流行,另一方面,产品本身的Bug也比较多,参考资料也比较少。我们再利用XDE进行开发的时候,虽然为其强大的性能所折服,但也总是有一些小毛病让人很烦。不过,XDE正处在不断的完善过程中,相信其下一个基于Eclipse2.0的版本会更加的优秀。

 

版权所有:UML软件工程组织