使用 XML: UML、XMI 和代碼生成,第 2 部分

本專欄當前的話題是建模、UML 和 XML。具體而言,即研究 UML 建模在 XML 開發中的應用,特別是如何用 XSLT 樣式表實現自動派生。

隨 着 XML 成爲開發項目的一種常見特性,很多開發人員對把 XML 和開發過程中的其他部分結合起來越來越感興趣。儘管許多組織仍然依靠專門的工具進行 XML 開發,但大的趨勢是在 XML 的開發中採用其他開發需求已經使用的方法論,或者至少是一組通用的工具,比如 Java 技術、數據庫和 Web。

自動派生


上一期已經指出,模型是幫助計算和預測的系統簡化描述。在本文的環境中,所謂系統指的都是 XML 詞彙表。

圖 1 表示的是模型連續體(continuum)的建模過程。第一個模型是畫在白板(或者一沓紙)上的,一般不那麼正規。這個階段的目標是讓所有的參與者(用戶、開發人員和設計人員)有機會自由地表達自己的觀點。

圖 1. 模型連續體

模型連續體

下一步是繪製一個 UML 模型(如果詞彙表很複雜可能要畫多個模型)。UML 模型更加精確和形式化,但仍然是綜合性的和可閱讀的,因爲其主要目的是作爲小組成員之間的交流工具。

最後一個模型是 XML 模式,這是最精確的模型。它的目標是使解析器能夠針對詞彙表的定義驗證 XML 文檔,因此在保持可讀性的同時更強調精確性。

這些模型之間的主要區別在於目標不同:從非正式的交流到通過解析器進行精確的、形式化的驗證。區別 在於模型的性質(XML 詞彙表的簡化描述),而在於模型提供的幫助層次。

既然把模型看作是從最不準確到最形式化的連續體,探討模型的 自動派生就合情合理了,即從早期模型自動生成新的模型的過程。顯然,只有當兩個模型具有等同的描述性時才能很好地自動派生,這和一些模型比另一些更富於描述性的觀點有幾分衝突。如何解決模型中的不同描述層次將在下一期討論,現在討論的重點是派生。







XML 元數據交換(XMI)


您也許還能記得上一期文章中,我通過 XMI 和 XSLT 實現了自動派生。假設您熟悉 XML 模式(否則請參閱 參考資料),我們用這一節來介紹 XML 元數據交換(XML Metadata Interchange,XMI)。

詞彙表和兼容性


XMI 是一個非常複雜的規範(1.2 版有 400 多頁),本文基於實現自動派生的需要做儘可能簡單的介紹。

XMI 並沒有規定一種 XML 詞彙表,而是定義了從元模型生成詞彙表的一種 算法。換句話說,XMI 沒有定義 ClassAttributeAssociation 或者其他您期望看到的標籤。相反,XMI 規定了如何爲元模型中的概念創建標籤。當然這需要處理大量的模型,先暫時忍耐一下--您很快就會明白。

因 此,與其說是詞彙表,XMI 更像是一個框架。不幸的是,這意味着不同的工具會以不同的方式解釋這個框架。即使同一種工具的不同版本也有差別:Rational Rose 最初通過 Unisys 開發的一個插件支持 XMI。最新版本的 Rational XDE 提供了 XMI 的內在支持,但卻是一個略有不同的變體。差別儘管不一定很大,但也可能造成不兼容。在實踐中,合理的做法是針對社區中使用的一兩種工具設計樣式表,而不必 顧及其他的工具。

本文中沒有采用特定的 XMI 版本,而是選擇採用 OMG 發表的例子。雖然沒有直接支持這些例子的工具,但作爲很好的起點,針對選用的工具進行適當調整並不困難。

XMI 頭


雖然 XMI 主要是規定了一種算法,但是也定義了少量的標籤和屬性。將要用到的包括:

  • XMI 一定是根元素。它必須包括 xmi.version 屬性(合法的版本有 1.0、1.1、1.2 和 2.0)。
  • XMI.header 用於存放關於模型的信息。最重要的子元素有 XMI.documentationXMI.metamodel
  • XMI.documentation 保存最終用戶的信息,其子元素有(這些子元素的名字含義非常明顯):
    • XMI.owner
    • XMI.contact
    • XMI.longDescription
    • XMI.shortDescription
    • XMI.exporter
    • XMI.exporterVersion
    • XMI.exporterID
    • XMI.notice
  • XMI.metamodel 記錄應用 XMI 算法的元模型--在這裏即 UML 元模型(XMI 也用於其他元模型,如同樣由 OMG 發佈的 Metaobject Facility,MOF)
  • XMI.content 包含實際的模型。
  • xmi.idxmi.idref 是用於鏈接的屬性: xmi.id 是一個元素標識符,必須是唯一的; xmi.idref 是通過標識符對元素的因素的引用。

元模型


UML 元模型是描述 UML 語言的模型--具體而言,它描述類、屬性、關聯、包、協作、用況、參與者、消息、狀態和 UML 語言中所有其他概念。爲了保持一致性,元模型使用 UML 編寫。

前綴“meta”表明元模型(metamodel)描述的是模型的模型。類似地,XML 是一種元語言,因爲它是一種描述語言的語言。

UML 元模型在 UML 規範中發佈。具體而言,XMI 使用 UML 規範(請參閱 參考資料)第5章中所描述的“UML Model Interchange”。

要注意,UML 元模型龐大得嚇人。本文中只能簡略地提一提。圖 2 摘自描述類的元模型,類是類圖中的核心概念。

圖 2. 類的元模型

類的元模型

這個元模型中,類的概念用元類 Class建模,後者繼承自抽象元類 Classifier。Classifier 是 Class、Interface 和 Datatype 的祖先(後兩者沒有在 圖 2中表示出來)。繼承鏈再往上推: GeneralizableElement代表所有可以泛化(被繼承)的概念; ModelElement代表模型中的所有抽象(如名稱空間、約束和類);最後是 Element,最高層次的元類。這些元類都有 Class 繼承的屬性。

XMI 的變體

少數供應商描述了他們的 XMI 變體。根本的辦法是建立一個小模型然後導出,在用文本編輯器打開文件查看。

XMI 元素和屬性( XMI.headerXMI.contentxmi.id )就像是文件中路標。

觀察元模型中的主要元素(如 Class、Attribute、Association),看看它們是如何映射到 XML 中的。如果手頭上有元模型的片段會非常方便。

區別只是表面性的:似乎沒有兩個應用程序使用同樣的名稱空間。一些應用程序使用 XML 元素編碼元模型中的屬性,而另一些則使用屬性(如 清單 1所示)。實際上,通過比較元模型很容易就看出其中的區別。

Classifier 和 Feature之間存在組合,後者是 StructuralFeature的祖先。 Attribute派生自 StructuralFeature。

這個元模型令您感到迷惑嗎?嘗試忘掉它是一個元模型,一個關於 UML 的元模型,把它看作是一個普通的模型吧。 圖 2簡單地指出了 Class 的概念,這是一種和接口以及數據類型有關(通過從 Classifier 繼承)的高度專業化的元素。Class 擁有名稱、可見性以及其他許多性質。最後,Class 和 Attribute 之間存在關聯。

因此, 圖 2形式化地表示一個類可以有名稱、可見性和其他特性,還可以具有屬性。事實上,圖 2 是 UML 類的定義。如果您覺得迷惑,可能是因爲這個定義本身是用 UML 編寫的!

爲了簡單起見,我在 圖 1中有意省略了名稱空間、約束、原型、繼承以及使類之所以成爲類的其他因素。請相信我,這些都包括在完整的 UML 元模型中,但是對本文而言沒有用處。

何必爲元模型費心呢?因爲在交給 XMI 算法時您得到的是 UML 的 XML 詞彙表。作爲一個例子,清單 1 是圖 3 的一種 XMI 表示(應用規範中所示範的 XMI 變體,參見 前述):

圖 3. 地址的 UML 模型

地址的 UML 模型


清單 1. 導出到 XMI 的地址
<?xml version="1.0"?>
<XMI xmi.version="1.2" xmlns:UML="org.omg/UML/1.4">
<XMI.header>
<XMI.documentation>
<XMI.exporter>ananas.org stylesheet</XMI.exporter>
</XMI.documentation>
<XMI.metamodel xmi.name="UML" xmi.version="1.4"/>
</XMI.header>
<XMI.content>
<UML:Model xmi.id="M.1" name="address" visibility="public"
isSpecification="false" isRoot="false"
isLeaf="false" isAbstract="false">
<UML:Namespace.ownedElement>
<UML:Class xmi.id="C.1" name="address" visibility="public"
isSpecification="false" namespace="M.1" isRoot="true"
isLeaf="true" isAbstract="false" isActive="false">
<UML:Classifier.feature>
<UML:Attribute xmi.id="A.1" name="name" visibility="private"
isSpecification="false" ownerScope="instance"/>
<UML:Attribute xmi.id="A.2" name="street" visibility="private"
isSpecification="false" ownerScope="instance"/>
<UML:Attribute xmi.id="A.3" name="zip" visibility="private"
isSpecification="false" ownerScope="instance"/>
<UML:Attribute xmi.id="A.4" name="region" visibility="private"
isSpecification="false" ownerScope="instance"/>
<UML:Attribute xmi.id="A.5" name="city" visibility="private"
isSpecification="false" ownerScope="instance"/>
<UML:Attribute xmi.id="A.6" name="country" visibility="private"
isSpecification="false" ownerScope="instance"/>
</UML:Classifier.feature>
</UML:Class>
</UML:Namespace.ownedElement>
</UML:Model>
</XMI.content>
</XMI>

要注意, 清單 1中的 XML 元素和屬性是如何與 圖 2中的類和屬性匹配的。您現在又回到了原地:XMI 文檔是 UML 元模型的直接表示,因爲 UML 元模型是 UML 本身的描述。

表示的問題


UML 元模型的一部分處理概念的可視化表示--把概念畫到屏幕的何處。由於兩方面的原因我的樣式表中沒有處理這些信息:

  • 從 UML 模型派生出 XML 模式不需要這些信息。
  • 從 XML 模式派生 UML 模型時生成可視化的輸出非常困難。更合理的選擇是用建模工具打開模型,花幾分鐘準備模型在屏幕上的可視化表示。最困難的工作(正確的定義)已經由樣式表完成了。






XSLT 樣式表


現在您已經掌握了閱讀 XMI 文件的要點,很容易就能把 XMI 標籤映射成等價的 XML 模式。一種可能的映射是:

  • UML:Model 變成 xs:schema ;目標名稱空間從模型名導出。
  • UML:Class 變成全局 XML 元素聲明( xs:element )。
  • UML:Attribute 變成本地 XML 元素聲明( xs:element )。

清單 2 是實現這種映射的 XSLT 樣式表:

清單 2. XML 模式派生
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:UML="org.omg/UML/1.4"
exclude-result-prefixes="UML"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="XMI[@xmi.version='1.2']">
<xsl:apply-templates select="XMI.content/UML:Model"/>
</xsl:template>
<xsl:template match="XMI">
<xsl:message terminate="yes">Unknown XMI version</xsl:message>
</xsl:template>
<xsl:template match="UML:Model">
<xs:schema targetNamespace="http://psol.com/uml/{@name}">
<xsl:apply-templates/>
</xs:schema>
</xsl:template>
<xsl:template match="UML:Namespace.ownedElement/UML:Class">
<xs:element name="{@name}">
<xs:complexType>
<xs:sequence>
<xsl:apply-templates/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xsl:template>
<xsl:template match="UML:Attribute">
<xs:element name="{@name}" type="xs:string"/>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
</xsl:stylesheet>

顯然, 清單 2中 的樣式表功能非常有限(只進行了很少的錯誤檢查),因爲它僅僅支持 UML 元模型很小的一個子集。它沒有考慮包、接口、關聯等等。只需要對上面描述的過程稍加擴展,就可以進一步完善這個樣式表以支持上述概念:研究 UML 元模型中相應的部分、定義到 XML 模式的映射然後實現它。

反過來一樣


如果遵循常規的建模流程,很容易得到 清單 2。您經常會發現已經存在一個 XML 模式,可以作爲工作的起點。重新建立 UML 模型太麻煩了,如果由一個樣式表實現逆映射會非常方便。清單 3 是一個例子:

請但 3. 逆派生(從 XML 模式到 UML)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:UML="org.omg/UML/1.4"
exclude-result-prefixes="xs"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="xs:schema">
<XMI xmi.version="1.2">
<XMI.header>
<XMI.documentation>
<XMI.exporter>dW simple stylesheet</XMI.exporter>
</XMI.documentation>
<XMI.metamodel xmi.name="UML" xmi.version="1.4"/>
</XMI.header>
<XMI.content>
<UML:Model xmi.id="{generate-id()}"
name="{substring-after(@targetNamespace,'http://psol.com/uml/')}"
visibility="public" isSpecification="false"
isRoot="false" isLeaf="false" isAbstract="false">
<UML:Namespace.ownedElement>
<xsl:apply-templates/> </UML:Namespace.ownedElement>
</UML:Model>
</XMI.content>
</XMI>
</xsl:template>
<xsl:template match="xs:element">
<UML:Class xmi.id="{generate-id()}" name="{@name}"
visibility="public" isSpecification="false" isRoot="true"
isLeaf="true" isAbstract="false" isActive="false">
<xsl:apply-templates/>
</UML:Class>
</xsl:template>
<xsl:template match="xs:sequence">
<UML:Classifier.feature>
<xsl:apply-templates/>
</UML:Classifier.feature>
</xsl:template>
<xsl:template match="xs:sequence/xs:element">
<UML:Attribute xmi.id="{generate-id(.)}" name="{@name}"
visibility="private" isSpecification="false"
ownerScope="instance"/>
</xsl:template>
</xsl:stylesheet>







更復雜的樣式表


如果要說本文中介紹的樣式表很簡單,就太過謹慎了。這些例子一般不到 50 行,只能處理 UML 元模型的一個子集。實際的樣式表能夠識別多得多的 UML 概念,通常需要 500 行甚至更多。本期文章的目標是介紹自動化模型派生背後的概念:

  • 這些模型(UML、XML 模式)都用一個數據集表示,這種特殊的數據集稱爲元模型。
  • 可以建立 UML 元模型和 XML 模式之間的映射。
  • 可以通過 XSLT 樣式表實現這種映射。
  • UML 和 XML 模式僅僅是同一事實的不同表示,區別在於服務於不同的目標。

本文中,我不得不作一些簡化。如果您嘗試擴展 清單 2清單 3中的樣式表,可能會遇到兩個問題:

  • UML 模型可能不夠具體(因爲是一種高級視圖),無法派生出有意義的 XML 模式(低級的、詳細的模型)。
  • UML 元模型和 XML 模式之間可能存在多種合理的映射。

如何解決這兩個問題在後面兩期文章中討論。



參考資料

  • 您可以參閱本文在 developerWorks 全球站點上的 英文原文.

  • 請參與本文的 討論論壇。(您也可以單擊本文頂端或下端的 討論來訪問論壇。)



  • 在對象管理組織(OMG)的網站上閱讀 UML 規範,其中包括完整的 UML 元模型。



  • 同樣在 OMG 網站上可以找到 XMI 規範,其中包括一些 UML 數據的樣例。雖然沒有完全支持這些例子的工具,但可以作爲一個很好的起點,很容易針對各種工具的特點進行調整。



  • 回顧本專欄的上一期文章“ UML, XMI, and code generation, Part 1”( developerWorks,2004 年 3 月),那篇文章中 Benoit Marchal 討論了 UML 和 XML 模式的關係。



  • 如果不熟悉 XML 模式,教程“ XML Schema Infoset Model”( developerWorks,2004 年 11 月)可以幫助您快速入門。



  • 研究 IBM Rational Rose,一流的 UML 建模產品。在 developerWorksRational 欄目中可以找到大量關於 Rational 和 UML 的資源。



  • developerWorks XML 專區 可以找到數百篇 XML 資源,包括 Benoit Marchal 使用 XML 專欄以前的各期文章。



  • 瞭解如何才能成爲一名 IBM 認證的 XML 及相關技術的開發人員
原文http://www.ibm.com/developerworks/cn/xml/x-wxxm24/ 
發佈了29 篇原創文章 · 獲贊 4 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章