MDA中模型的存儲、表現與轉換
最近一些朋友在我的blog上面留言或者給我寫email討論一些問題,本文希望能夠一起解答他們的一些疑問,同時也整理一下自己的思路。
MDA是以模型為中心的,模型是其基本元素,所以關于模型的存儲、查詢、表現和轉換是對于基本元素的操作。OMG已經有一些既定規范或者正在制訂的規范是用來討論這些問題的。例如XMI規范規定了模型的存儲格式、QVT規范(正在制訂中,已經有一個初步的版本)是規定模型的查詢、視圖和轉換的。最近的QVT規范對于模型的查詢尚未涉及,因此本篇中也不討論模型查詢。重點總結一下模型的存儲、表現(視圖)和轉換。并力所能及的舉例來說明。
模型的存儲
我認為模型的存儲按照其發展進程來看可以分為以下幾種:自定義格式;基于XML的自定義格式;遵守XMI的格式;可以轉換為其它格式的腳本格式;以及直接以代碼存儲的形式。
我對于軟件建模的了解是從UML開始的,UML統一之前的各種建模方法并未研究,相信在UML統一之前,模型是沒有一個統一的存儲格式的。其必然是按照自定義的格式來存儲的。不同的建模工具是按照自己定義的格式來存儲模型,對于以下一個最簡單的模型:

一個建模工具可以這樣存儲:
類:Bank
屬性:name;類型:string
連接:customers;類型:Customer
類:Customer
屬性:name;類型:string
屬性:age;類型:int
屬性sex;類型:boolean
連接:bank;類型:Bank
簡而言之,只要建模工具能夠識別,模型的存儲格式是沒有限制的。
但是,這種自定義的方式顯然是有缺點的,最大的兩個確定就是不具有可交互性,不能被其它工具識別。目前的建模工具已經不使用這樣隨意的方法了。
交互性好,能夠廣泛的被工具識別的XML技術恰好與建模技術的發展在同步進行。大概都在2000年左右,UML和XML幾乎同時達到了最高點,因此現在的建模工具使用基于XML的模型存儲格式也就不是一件意外的事情了。由于幾乎所有的建模工具都采用了XML作為模型的存儲方式,OMG特別制訂了一個XMI規范,專門規定了模型的XML存儲格式。XMI事實上是一個XML Schema,它規定了模型、對象等概念的存儲格式。
目前很多建模工具都支持XMI2.0的存儲,例如EMF、MagicDraw(RationalRose我沒有看,估計支持,因為和EMF一樣都是IBM的一母同胞)。下面是EMF的存儲格式,同樣是上面的那個模型:
<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="mypackage">
<eClassifiers xsi:type="ecore:EClass" name="Bank">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="customer" lowerBound="1"
upperBound="-1" eType="#//Customer"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Customer">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="age" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="sex" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"/>
</eClassifiers>
</ecore:EPackage>
可以看出來,EMF使用了XML1.0和XMI2.0,EMF的模型存儲是比較簡化的,可以人工閱讀和修改。MaigicDraw以及Rose的模型存儲則非常繁瑣,包含了眾多圖信息(所謂圖信息,就是定義了可視化模型元素的位置、大小、顏色等的信息)、自定義信息等,是不支持人工閱讀的。例如上面那個模型的MagicDraw的模型存儲文件如果不壓縮的話有174kb。
也有一些工具使用類型于腳本的語言來定義和存儲模型,這種腳本一般都類似java語言或者idl等語言,可以方便的定義模型元素。也能夠被方便的轉換為不同工具支持的模型。例如ATL自帶的km3語言。當然,這樣總是覺得有點怪怪的。明明使用模型就是為了避免從代碼開始,但是定義模型竟然又使用了另一種代碼。上面的模型使用km3的格式如下:
package mypackage {
class Bank {
attribute name[0-1] ordered : EString
reference customer[1-*] ordered : Customer;
}
class Customer {
attribute name[0-1] ordered : EString
attribute age[0-1] ordered : EInt
attribute sex[0-1] ordered : EBoolean
reference bank[0-1] ordered : Bank;
}
}
最后,用代碼也可以存儲模型,EMF的一種模型存儲方式就是帶有標記的java代碼來存儲模型,所以上面的模型也可以使用如下代碼存儲:
public class Bank {
private String name;
private Customer customers;
}
public class Customer {
private String name;
private int age;
private boolean sex;
private Bank bank;
}
Together這樣的工具根本就把模型和代碼看成同一件事物的兩種外表,因此,用代碼做為存儲模型的格式也是自然而精確的。不過這樣也是有缺點的,就是圖信息無法保存,而且要確保代碼生成是毫無疑義的。
模型的表現
在QVT(模型的查詢、視圖、轉換)規范中使用的是View這個術語,它的含義是模型的表現或者視圖。在最近的QVT規范中,還不支持視圖的創建和管理。但是視圖的概念類似與MVC中的視圖概念。我試著下一個定義:模型的視圖指的是對于軟件模型的某個側面在某種場景下的一種表現方式。例如,類圖是對于某個軟件的靜態結構的視圖,而對象圖是軟件運行時的某個時刻的視圖。
視圖的另外一層含義是,如何將存儲好的模型以可視化的方式展現在用戶面前。有人說,最好的建模工具是白紙和筆,這句話也是有一定道理的。用白紙和筆可以很快的畫出可以用來交流的UML圖。此時,白紙上的UML圖就是模型的一個視圖。UML為模型的視圖提供了一組標準化的符號和概念,讓用戶可以使用不同的建模工具構建具有相同含義的軟件模型,即使它們的顏色、大小、方位以及存儲格式不同,它們指的是同樣的內涵。
一個模型的信息只有一個,但是其表達方式是無窮的,每種表達方式都可以稱為此模型的一個視圖,它們通常是白紙上畫出的模型,或者建模工具中的圖形,或者規范的XML文件等等。
舉例說明,上面例子模型中,其類圖是一種視圖,我們也可以規定另一種視圖如下:
每個類用綠色的方塊表示,方塊中上部寫類名稱,中下部寫屬性名稱,屬性后面必須跟一個冒號,冒號后面是屬性的類型……
模型的轉換
在看透了模型存儲和視圖的本質以后,再來討論模型的轉換就比較容易了。模型的本質是什么呢?是其存儲格式還是其表現形式呢?都可以!根據其存儲格式或者視圖都可以進行模型轉換。
最簡單的模型轉換例子就是xslt了。以XML格式存儲的模型,一個XSLT的入門者就可以寫出模型轉換的實例。
例如我們要將上面的EMF的XML存儲文件轉換一下,將Bank類的name屬性名改為bankname,則使用下面的xsl文件可以轉換:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore">
<xsl:template match="/">
<xsl:apply-templates select="ecore:EPackage"/>
</xsl:template>
<xsl:template match="ecore:EPackage">
<xsl:text disable-output-escaping="yes">
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
</xsl:text>
name= "<xsl:value-of select="@name"/>"
<xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:apply-templates select="eClassifiers"/>
<xsl:text disable-output-escaping="yes">
</ecore:EPackage>
</xsl:text>
</xsl:template>
<xsl:template match="eClassifiers">
<xsl:choose>
<xsl:when test="@name[. = 'Bank']">
<eClassifiers xsi:type="ecore:EClass" name="Bank">
<xsl:for-each select="eStructuralFeatures ">
<xsl:choose>
<xsl:when test="@name[. = 'name']">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="bankname" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</eClassifiers>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
其中<xsl:template match="ecore:EPackage">可以寫得更簡潔一點,直接使用源文件中的這個部分來代替,但是為了體現屬性的讀取功能,所以使用了xsl:value-of來讀取屬性,并填入ecore:EPackage的標記中,同時因為標記不能封閉,所以使用了xsl:text來輸出。運行這個xsl轉換源模型就可以得到目標模型。
源模型和目標模型在EMF中的顯示如下:


左邊的是源模型,右邊則是目標模型。從上面簡單的例子可以看出,模型轉換可以使用XSLT對以xml方式存儲的模型進行轉換。
但是,以XSLT來進行模型轉換是不自然而且繁瑣的。我們需要更好的模型轉換方法。這時的一個思路就是能否把模型載入內存,然后用面向對象的方法以及語言來處理模型元素對象,然后生成目標模型。
(撰寫中。。。)