用C#+XMI技术进行UML模型捕获
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
有許多不同的方法可用于捕獲XML模型數(shù)據(jù)并且把它放到一個(gè)數(shù)據(jù)存儲(chǔ)中,正如你所期望的,包括使用XSLT技術(shù)。但是我想使用一種不同的方法-使用C#語言。XSLT是一個(gè)用于改變XML文件的好選擇,但是對(duì)于更廣闊的不僅僅是轉(zhuǎn)變數(shù)據(jù)的應(yīng)用軟件來說,C#或者另外的象Java這樣的高級(jí)語言提供了更大的靈活性。
在本文中,我將展示如何通過使用XMI和C#來剖析一個(gè)UML發(fā)布圖。首先,我展示一個(gè)該方法的簡(jiǎn)單的發(fā)布圖,所在環(huán)境為一個(gè)虛構(gòu)的汽車出租公司并使用 C#來捕獲一些數(shù)據(jù)。這種數(shù)據(jù)可以被容易地加到一個(gè)數(shù)據(jù)庫(它已經(jīng)是ADO調(diào)用的相當(dāng)容易的一部分)上去或者作為更大些的一個(gè)基于資產(chǎn)的管理系統(tǒng)的一部分。在每一個(gè)示例中,我將逐漸增加原始圖和能夠獲取的信息的復(fù)雜性。
一、 示例1-查找方法的名字
我要做的第一件事情是捕獲來自于汽車出租公司的所有方法的名字。共有五個(gè)方法,它們是以UML發(fā)布圖形描述的(見圖1)。在上一篇文章中,我展示了如何用一個(gè)XML文檔來描述一個(gè)發(fā)布圖。為此,首先要?jiǎng)?chuàng)建該圖,然后把它輸出到一個(gè)XMI文件(企業(yè)架構(gòu))或解壓(MagicDraw)。(在這篇文章中,我再次使用了SparxSystems.com提供的企業(yè)架構(gòu)。)
圖1.五個(gè)方法:圖中結(jié)點(diǎn)描述了汽車出租公司的方法。每一個(gè)結(jié)點(diǎn)為一個(gè)方法名字。
我將首先開發(fā)一簡(jiǎn)單main程序-它提示用戶輸入描述一個(gè)有效的XMI文檔的XML文檔的名字。一旦輸入文件名字,它就被傳遞到一個(gè)分析XMI文檔的對(duì)象并打印輸出方法名字。
using System;
using System.Text;
namespace XMI_1{
public class ConsoleUtils{
public static string ReadString(string msg) {
Console.Write(msg); return System.Console.ReadLine();
}
public static void WriteString(string msg)
{ System.Console.WriteLine(msg); }
public static void Main(string[] args) {
string name = ReadString("Please enter the XMI filename : ");
NodeParse np = new NodeParse(name);
System.Console.ReadLine();
}
}
}
下一步是分析XMI文檔。為此,我必須創(chuàng)建一個(gè)XMLTextReader的實(shí)例并循環(huán)操作直到?jīng)]有結(jié)點(diǎn)為止。XMLNodeType.Element 檢查每一個(gè)XML元素并且在元素是一個(gè)結(jié)點(diǎn)的情況下把它打印輸出到控制臺(tái)。該元素名字資格為"UML:Node",然而通過使用 _readXMI.LocalName只有元素名字的"Node"被讀取。詳細(xì)代碼參見列表1。
二、 示例2-增加模板
第二個(gè)示例重構(gòu)了第一個(gè)示例來捕獲方法的名字和"pc server"模板-這是一個(gè)用來決定每個(gè)結(jié)點(diǎn)是一個(gè)方法的UML方法。圖3顯示增加了模板后的方法的發(fā)布圖。
第一步是修改前面的代碼以識(shí)別新的模板來操縱AddNode()方法。
圖3.增加了模板之后:每個(gè)模板描述了結(jié)點(diǎn)的類型-不管它是一個(gè)PC服務(wù)器,PC客戶端或者是另外一些描述該結(jié)點(diǎn)的目標(biāo)。
private void AddNode(XmlTextReader p_readXMI)
{
string nodeID;
string nodeName;
string stereotypeName;
nodeID = p_readXMI.GetAttribute("name");
nodeName = p_readXMI.GetAttribute("xmi.id");
Console.Write(nodeID);
Console.Write(" -> ");
Console.WriteLine(nodeName);
while (p_readXMI.Read() && (p_readXMI.NodeType == XmlNodeType.Element ||?
p_readXMI.NodeType== XmlNodeType.Whitespace))
{
switch (p_readXMI.LocalName)
{
case "ModelElement.stereotype":
while (p_readXMI.Read() && (p_readXMI.NodeType == XmlNodeType.Element ||?
p_readXMI.NodeType == XmlNodeType.Whitespace))
{
if (p_readXMI.LocalName == "Stereotype")
{
stereotypeName = p_readXMI.GetAttribute("name");
Console.WriteLine("Stereotype = " + stereotypeName);
}
}
break;
}
}
}
特別要注意的是一個(gè)idiom(對(duì)某個(gè)語言特有的一個(gè)低級(jí)別的模式)-我用來查找在另外一些元素內(nèi)部的XML元素。我在p_readXMI.Read循 環(huán)中增加了代碼來檢查元素或者空格(在XML文檔中,這是些空格字符)。只有每個(gè)元素結(jié)點(diǎn)在XML文檔中適當(dāng)?shù)膶哟紊咸幚頃r(shí),這個(gè)idiom才工作。
用這種方式對(duì)元素進(jìn)行封裝,結(jié)果不很理想。例如,在下面的UML中使用idiom代碼將產(chǎn)生所不希望的結(jié)果:
?
| <UML:Node name="Leasing"> <UML:TagName name="Test"> </UML:Node name="SmallLeasing"> </UML:TagName> </UML:Node> |
?
|
|
第一個(gè)元素被作為一個(gè)結(jié)點(diǎn)處理,第二個(gè)元素被忽略并在初始循環(huán)中退出,而第三個(gè)元素被作為在同一級(jí)上的第一個(gè)正常結(jié)點(diǎn)處理。為了修正這個(gè)問題,你可以使 用XMLNodetype.Endelement來檢查元素的結(jié)束標(biāo)簽。在本文中,這不是個(gè)問題而保存額外的代碼要求檢查結(jié)尾元素。
XMI中的模板被封裝在結(jié)點(diǎn)元素中。該示例中的代碼檢查名字為"ModelElement.Stereotype"的元素并且使用剛才討論的相同的idiom來處理一個(gè)"模板"類型的封裝元素,然后把它打印輸出到屏幕上(見圖4)。(列表3列出了示例2的完全的源碼。)
三、 示例3-添加硬件信息
從資產(chǎn)管理的角度來看,捕獲方法的名字和它們的模板并沒有多大用處。當(dāng)然,另一方面,如果你的圖包含了描述正在使用的硬件信息也可能是非常有用的:例如,基于UML發(fā)布圖把你所有的硬件保存到一個(gè)數(shù)據(jù)庫將能夠使你跟蹤方法、客戶及其如何進(jìn)行彼此聯(lián)系的。
在UML中,硬件結(jié)點(diǎn)都貼有用來識(shí)別它們屬性的"標(biāo)簽"。每一個(gè)標(biāo)簽都是在建模工具(這里是企業(yè)架構(gòu))中產(chǎn)生而且由建模器所定義(或者有時(shí)保存成一個(gè) UML剖面文件)。在本示例中,我為CPU、磁盤大小、內(nèi)存大小、目的、注意事項(xiàng)以及賣主等創(chuàng)建了標(biāo)簽(見圖5)。每個(gè)標(biāo)簽具有一個(gè)值-或者被賦予一個(gè)基 本類型(字符串,整型……)或者從一個(gè)可用值列表中選擇其一。無論如何,保持與所用值的一致性是很重要的。如果你把"GHT"用于CPU以描述 "gigahertz hyper-threaded",那么對(duì)每個(gè)CPU標(biāo)簽,你都要使用相同的約定。
?
|
|
新的重構(gòu)的代碼與以前的一樣,但是增加了讀取標(biāo)簽的代碼。在此,XMI并沒有如你所盼的那樣封裝結(jié)點(diǎn)內(nèi)的標(biāo)簽。作為代替,每個(gè)屬性是一個(gè) "TaggedValue"元素-它通過使用"modelElement"屬性來參考引用屬性的結(jié)點(diǎn)標(biāo)識(shí)符(XML.id)。這樣做的困難在于結(jié)點(diǎn)元素必 須在標(biāo)簽元素之前被讀取,并且每個(gè)結(jié)點(diǎn)元素必須被保存-為使標(biāo)簽元素依附于其上。
在XML中,存在兩種讀取文檔的方法。第一種是讀取 完整的文檔并把它以一棵樹保存到內(nèi)存中-這里每個(gè)元素是從根元素開始構(gòu)建的層次結(jié)構(gòu)的一部分。這就是DOM模型,是較佳的適用于小型文檔的方法。第二種方 法是,讀取文檔時(shí),每次分析一個(gè)元素。SAX就是這種方法的一個(gè)示例并且它被當(dāng)作推模型,因?yàn)橛伤治鑫臋n并返回分析后的文檔(推它)而不需要提示。
另外一種方法是微軟的XMLReaders(XMLTextReader派生于它)-它是一個(gè)拉模型,因?yàn)楫?dāng)下一個(gè)元素被分析時(shí),控制掌握在程序中。我 在此使用的XML文檔很小,但是我分析過的一些文件是超過了500,000行的文本文件,這導(dǎo)致我求助于XMLReader方法。這種方法的一個(gè)不足是要 求結(jié)點(diǎn)應(yīng)出現(xiàn)在標(biāo)簽元素之前。
為了保存結(jié)點(diǎn)我需要使用一個(gè)鍵/值容器。最易于使用的是Hashtable。在重構(gòu)主程序中(見下),我使用了Hashtable中的枚舉能力以及用IDictionary枚舉器來打印方法結(jié)點(diǎn)。
?
| public static void Main(string[] args){ Hashtable mainHash; IDictionaryEnumerator ienum; Node tNode; string name = ReadString("Please enter the XMI filename : "); NodeParse np = new NodeParse(name); mainHash = np.getNodes(); ienum = mainHash.GetEnumerator(); while (ienum.MoveNext()) { tNode = (Node)ienum.Value; System.Console.WriteLine("Node ="+tNode.name+" CPU= "+tNode.CPU); } System.Console.ReadLine(); } |
我重構(gòu)了nodeParse對(duì)象以用于檢查元素"TaggedValue"并且調(diào)用AddAttributeNode-它負(fù)責(zé)在哈希表中查找正確的結(jié)點(diǎn)并且通過一個(gè)case語句把標(biāo)簽添加到該結(jié)點(diǎn)上。相應(yīng)的類NodeParse顯示于列表4中。
這個(gè)結(jié)點(diǎn)類僅僅是一個(gè)存儲(chǔ)狀態(tài)的對(duì)象。每一個(gè)標(biāo)簽都有它自己的屬性。注意,這個(gè)結(jié)點(diǎn)類的構(gòu)造器要求該對(duì)象必須用服務(wù)器名字初始化。?
?
| using System; using System.Text; namespace XMI_1 { class Node{ string _name,_id = "",_CPU,_MemorySize,_DiskSize,_Note,_Purpose, _Stereotype,Vendor; public Node(string p_id) {this._name = p_id;} public string id {get { return _id;} public string name{get {return _name;} set {_name = value;}} public string CPU {get { return _CPU;} set {_CPU = value;}} public string MemorySize{get {return _MemorySize;} set {_MemorySize = value;}} public string DiskSize {get {return _DiskSize;}set {_DiskSize = value;}} public string Notes{get {return _Notes;}set {_Notes = value;}} public string Purpose{get {return _Purpose;} set {_Purpose = value;}} public string Stereotype {get {return _Stereotype;} set {_Stereotype = value;}} public string Vendor {get {return _Vendor;} set {_Vendor = value;}} } } |
我并沒有提供示例3和輸出結(jié)果,因?yàn)樗鼛缀鹾褪纠?的完全相同。(列表5列出了示例3的完整的源代碼。)
四、 示例4-添加繼承
在最后的示例中,我從一個(gè)Node類繼承了結(jié)點(diǎn)實(shí)例。以前,我們每次只分析一個(gè)結(jié)點(diǎn)并對(duì)其進(jìn)行遍歷。Node實(shí)例描述了存在于UML結(jié)點(diǎn)和硬件之間的關(guān)系。Nodes給了我們一種從實(shí)例中抽象出公共元素的方法。
為此,有兩種不同的方法。第一種方法是使用Nodes來描述一個(gè)通用硬件平臺(tái)??梢栽O(shè)想這樣的情形:我為一家公司工作,該公司想針對(duì)它們所有的方法(多 么奇怪!)訂購相同的計(jì)算機(jī)配置。可以用一個(gè)結(jié)點(diǎn)來描述典型的計(jì)算機(jī)配置,然后該結(jié)點(diǎn)又會(huì)有多個(gè)結(jié)點(diǎn)實(shí)例。這將節(jié)省大量的輸入時(shí)間!另外一種方法是在軟件 架構(gòu)師通知基礎(chǔ)構(gòu)件小組怎樣分發(fā)組件的情形。該結(jié)點(diǎn)用針對(duì)于每個(gè)層的本地名字來描述不同的層。至于這些如何映射到實(shí)際的硬件是由結(jié)點(diǎn)實(shí)例所決定的而且由基 礎(chǔ)構(gòu)件小組所創(chuàng)建。?
在該示例中(見圖6),我展示了一個(gè)通用結(jié)點(diǎn)-"Fleet Management",它具有可以添加到它上面的組件。在一個(gè)多層系統(tǒng)中,Fleet Management是由軟件架構(gòu)師來定義成一個(gè)分離的層。為了說明問題,我可能還要應(yīng)用"Purpose"標(biāo)簽。我有兩個(gè)結(jié)點(diǎn)實(shí)例,"Trucks"和 "Persons",它們由基礎(chǔ)構(gòu)件小組來定義以把該層分成兩個(gè)方法。既然"Trucks"和"Persons"是完全不同的兩個(gè)域,那么架構(gòu)師介入其中 并分解之是十分安全的。方法名字上還標(biāo)記有":Fleet Management",以指明它們是在Fleet Management結(jié)點(diǎn)中實(shí)現(xiàn)的。
?
|
|
在這個(gè)示例中,沒有用于繼承的標(biāo)簽,但是可能在另外的圖上存在一些其它關(guān)系-它們會(huì)連接到該結(jié)點(diǎn)實(shí)例上。
我又一次重構(gòu)了main程序以打印出所有的,包括每一個(gè)結(jié)點(diǎn)實(shí)例派生的(tNode.classname)結(jié)點(diǎn)標(biāo)簽,還有結(jié)點(diǎn)的ID(tNode.classifier)。
?
| public static void Main(string[] args) { Hashtable mainHash; IDictionaryEnumerator ienum; Node tNode; string name = ReadString("Please enter the XMI filename : "); NodeParse np = new NodeParse(name); mainHash = np.getNodes(); ienum = mainHash.GetEnumerator(); while (ienum.MoveNext()){ tNode = (Node)ienum.Value; System.Console.WriteLine("Node = " + tNode.name); //Add "if (tNode. != null)" for each Writeline()? System.Console.WriteLine(" CPU = " + tNode.CPU); System.Console.WriteLine(" DiskSize = " + tNode.DiskSize); System.Console.WriteLine(" MemorySize = " + tNode.MemorySize); System.Console.WriteLine(" Purpose = " + tNode.Purpose); System.Console.WriteLine(" Notes = " + tNode.Notes); System.Console.WriteLine("Node refers to = " + tNode.classname); System.Console.WriteLine("Node addr is " + tNode.classifier); System.Console.WriteLine(); } System.Console.ReadLine(); } |
類名和類標(biāo)志符指明該結(jié)點(diǎn)實(shí)例繼承自哪些結(jié)點(diǎn)。在XMI中,這是被象一個(gè)模板(就象一個(gè)封裝在結(jié)點(diǎn)元素中的元素)一樣描述的。
現(xiàn)在我將使用一個(gè)開關(guān)語句來檢查"ModelElement.taggedValue",而代之以ModelElement.Stereotype,查 找"TaggedValue"元素(存在類名和類標(biāo)志符上皆獨(dú)立的元素),并把它們添加到結(jié)點(diǎn)對(duì)象上。在此,我再次使用了分析idiom。這些代碼可以在 列表3中找到。
圖7.Trucks和Persons:示例4的最后輸出,Trucks和Persons各有一個(gè)它們可以參考的結(jié)點(diǎn)并且每個(gè)結(jié)點(diǎn)都有一個(gè)唯一的ID。
圖7顯示示例4最后的輸出。
五、 總結(jié)
?
在本文中,我討論了可以從圖中讀取硬件資產(chǎn)的方法。還討論了讀取名字、模板、屬性以及讀取結(jié)點(diǎn)之間及結(jié)點(diǎn)實(shí)例(繼承)之間的關(guān)系。盡管我沒有給出把這種信息放入數(shù)據(jù)庫中的顯式代碼,我將在后面的文章中討論這些技術(shù)-當(dāng)然不止是添加信息到數(shù)據(jù)庫中。
?
登錄樂搏學(xué)院官網(wǎng)http://www.learnbo.com/
或關(guān)注我們的官方微博微信,還有更多驚喜哦~
?
本文出自 “青峰” 博客,轉(zhuǎn)載請(qǐng)與作者聯(lián)系!
轉(zhuǎn)載于:https://my.oschina.net/learnbo/blog/829239
總結(jié)
以上是生活随笔為你收集整理的用C#+XMI技术进行UML模型捕获的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在eclipse中安装propertie
- 下一篇: c# char unsigned_dll