第三部分: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的版本會更加的優秀。
參考文獻:
1.《UML參考手冊》 機械工業出版社
2.《UML用戶指南》 機械工業出版社
3.《設計模式:可復用面向對象軟件的基礎》機械工業出版社
4.Rational XDE在線文檔,其中可以得到XDE使用的大部分介紹
5.Rational.net開發者網站:www.rational.net。有許多關於XDE的功能的詳細文檔