楊卓卿
(重慶郵電大學(xué)軟件工程學(xué)院,重慶400065)
在軟件開(kāi)發(fā)領(lǐng)域,統(tǒng)一建模語(yǔ)言(Unified Modeling Language,UML)被廣泛認(rèn)為是一種標(biāo)準(zhǔn)化的、通用的面向?qū)ο蠼UZ(yǔ)言。盡管如此,UML對(duì)客觀(guān)世界的描述能力有限,某些常見(jiàn)的語(yǔ)義,在UML中還沒(méi)有足夠的元模型來(lái)解釋[1]。
關(guān)聯(lián)的概念是大多數(shù)建模語(yǔ)言的一個(gè)關(guān)鍵元素,在概念模型甚至是程序代碼中,關(guān)聯(lián)定義了類(lèi)之間的關(guān)系。在對(duì)客觀(guān)世界進(jìn)行建模時(shí),最常見(jiàn)的是二元關(guān)聯(lián),即維系兩個(gè)類(lèi)的關(guān)聯(lián),此外,一元關(guān)聯(lián)也不可忽視,顧名思義,一元關(guān)聯(lián)只涉及一個(gè)類(lèi),也叫自關(guān)聯(lián)。在現(xiàn)實(shí)生活中,普遍存在一種特殊的一元關(guān)聯(lián),其特點(diǎn)是關(guān)聯(lián)雙方所扮演的角色是對(duì)等的。例如,人與人之間互為鄰居的關(guān)系、一個(gè)數(shù)學(xué)函數(shù)與它的反函數(shù)存在互為反函數(shù)的關(guān)系等,這種關(guān)聯(lián)叫做對(duì)稱(chēng)一元關(guān)聯(lián)[2]。然而,主流的建模語(yǔ)言UML缺少對(duì)稱(chēng)一元關(guān)聯(lián)語(yǔ)義的支持。
對(duì)此,本文首先分析了對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義,根據(jù)語(yǔ)義抽象出了對(duì)稱(chēng)一元關(guān)聯(lián)的集合、圖等數(shù)學(xué)模型,再結(jié)合數(shù)學(xué)模型和UML類(lèi)圖元模型,分析了UML無(wú)法支持對(duì)稱(chēng)一元關(guān)聯(lián)的原因,以及由此引發(fā)的代碼生成問(wèn)題。然后討論了對(duì)稱(chēng)一元關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu),提出通過(guò)擴(kuò)展UML類(lèi)圖元模型使UML支持對(duì)稱(chēng)一元關(guān)聯(lián)建模的方法,并針對(duì)每種數(shù)據(jù)結(jié)構(gòu)給出代碼生成算法,最后通過(guò)實(shí)際例子的應(yīng)用驗(yàn)證了該方法的可用性和有效性。
模型驅(qū)動(dòng)架構(gòu)(Model Driven Architecture,MDA)是對(duì)象管理組織(Object Management Group,OMG)提出的一個(gè)軟件開(kāi)發(fā)方法。所謂模型驅(qū)動(dòng),就是一種用模型來(lái)指導(dǎo)軟件工程師對(duì)軟件系統(tǒng)的理解、設(shè)計(jì)、構(gòu)造、部署、操作、維護(hù)和改進(jìn)的方法[3]。
為了描述元建模的機(jī)制和方法,OMG定義了MDA的四層元模型體系結(jié)構(gòu)[4],分別是M0層、M1層、M2層、M3層。M0層是信息層,也叫客觀(guān)世界;M1層是模型層,用于描述M0層的信息;M2層是元模型層,用于描述M1層的模型;M3層是元元模型層,其元素提供了定義元模型的基本結(jié)構(gòu)。從M0層到M3層是一個(gè)逐層抽象的過(guò)程。
在MDA中,不同的應(yīng)用場(chǎng)景建立不同的模型,不同的模型由不同的元模型來(lái)描述。類(lèi)圖元模型是描述類(lèi)圖的模型,其作用是為類(lèi)圖的建立定義一系列建模元素,稱(chēng)之為類(lèi)圖元模型中的“元元素”。在類(lèi)圖元模型中,用一個(gè)名為“Association”的元元素來(lái)描述類(lèi)圖中的“關(guān)聯(lián)”。類(lèi)圖中的“關(guān)聯(lián)”都是類(lèi)圖元模型中的“Association”元元素實(shí)例化后的對(duì)象。
UML標(biāo)準(zhǔn)文檔中對(duì)關(guān)聯(lián)的定義是“一種在類(lèi)型化實(shí)例之間發(fā)生的語(yǔ)義關(guān)系”[5]。一個(gè)關(guān)聯(lián)至少有兩個(gè)端點(diǎn),稱(chēng)為關(guān)聯(lián)端[1],關(guān)聯(lián)端的名稱(chēng)稱(chēng)為角色,每一端與該關(guān)聯(lián)涉及的一個(gè)類(lèi)相連接。關(guān)聯(lián)端的多重性表示關(guān)聯(lián)的某一端所連接的類(lèi)對(duì)象的數(shù)量,多重性的值可以是一個(gè)特定的值,也可以是一個(gè)整數(shù)區(qū)間。關(guān)聯(lián)端可以是可導(dǎo)航的或不可導(dǎo)航的,可導(dǎo)航端所連接的類(lèi)的對(duì)象將隱式地成為另一端所連接的類(lèi)的成員屬性,稱(chēng)為偽屬性[6]。單向關(guān)聯(lián)的關(guān)聯(lián)端的其中一端是可導(dǎo)航的,雙向關(guān)聯(lián)的兩個(gè)關(guān)聯(lián)端都是可導(dǎo)航的[7]。
本節(jié)將討論對(duì)稱(chēng)一元關(guān)聯(lián)存在的問(wèn)題及其語(yǔ)義,然后討論實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu),就存在問(wèn)題提出了擴(kuò)展UML類(lèi)圖元模型的方法和代碼生成算法。
對(duì)稱(chēng)一元關(guān)聯(lián)在現(xiàn)實(shí)生活中普遍存在,文獻(xiàn)[2]中使用的概念建模語(yǔ)言(Conceptual Modelling Language,ConML[8])提供了對(duì)稱(chēng)一元關(guān)聯(lián)的建模所需的元模型和圖形符號(hào),但是,UML作為一套被軟件開(kāi)發(fā)人員廣泛使用的建模語(yǔ)言,其元模型并不支持對(duì)稱(chēng)一元關(guān)聯(lián)的建模。
在理論上,如果用UML類(lèi)圖表示對(duì)稱(chēng)一元關(guān)聯(lián),不僅關(guān)聯(lián)兩端連接到同一個(gè)類(lèi),而且其關(guān)聯(lián)兩端的角色名、多重性、可導(dǎo)航性完全相同。以人與人之間的鄰居關(guān)系為例,每一個(gè)人都可以有多個(gè)鄰居,且鄰居的關(guān)系是相互的、對(duì)等的,在一個(gè)鄰居關(guān)系中,每一方都是另一方的鄰居。圖1(a)是用UML類(lèi)圖表示的鄰居關(guān)聯(lián)。然而這將會(huì)與UML的建模規(guī)則產(chǎn)生矛盾。
圖1(c)是UML中關(guān)聯(lián)的元模型,用其表示“鄰居”關(guān)聯(lián)得到的對(duì)象圖如圖1(b)所示。圖1(b)中的對(duì)象圖出現(xiàn)了兩個(gè)完全相同的“neighbour”對(duì)象,然而在UML的標(biāo)準(zhǔn)文檔中,NamedElement元元素定義了一個(gè)名為isDistinguishableFrom()的方法來(lái)判斷UML模型中的兩個(gè)命名元素是否可以在同一個(gè)命名空間中邏輯共存[9],當(dāng)兩個(gè)命名元素是不同類(lèi)型的,或者同類(lèi)型不同名時(shí),兩者可以同時(shí)存在于同一個(gè)命名空間中。
類(lèi)圖中的關(guān)聯(lián)端的角色是由Property元元素實(shí)例化而來(lái)的,而Property元元素間接繼承了NamedElement元元素,因此關(guān)聯(lián)端的角色也遵循isDistinguishableFrom()方法的規(guī)則。可見(jiàn),圖1(b)中的兩個(gè)相同的neighbour對(duì)象不能同時(shí)存在。
由對(duì)稱(chēng)一元關(guān)聯(lián)的UML建模問(wèn)題,衍生出的是其代碼生成問(wèn)題。對(duì)稱(chēng)一元關(guān)聯(lián)兩端的角色既是相同類(lèi)型的,又有相同的名稱(chēng)和可導(dǎo)航性。在生成數(shù)據(jù)訪(fǎng)問(wèn)代碼時(shí),會(huì)導(dǎo)致語(yǔ)義的缺失,從而生成錯(cuò)誤的代碼。以“鄰居”關(guān)聯(lián)為例,如果按照UML現(xiàn)有的語(yǔ)義、約束和代碼生成規(guī)則,在將類(lèi)圖生成實(shí)體類(lèi)代碼時(shí),Person實(shí)體類(lèi)代碼中會(huì)出現(xiàn)兩個(gè)同是Person類(lèi)型且同樣名為neighbour的成員變量,其Java代碼如下。顯然這樣的代碼無(wú)法通過(guò)編譯。
圖1 用UML中關(guān)聯(lián)的元模型表示“鄰居”關(guān)聯(lián)
為了解決對(duì)稱(chēng)一元關(guān)聯(lián)無(wú)法使用UML建模的問(wèn)題,下面將從對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義著手,通過(guò)數(shù)學(xué)方法分析產(chǎn)生問(wèn)題的原因。
在UML中,類(lèi)是由一組擁有相同特征的對(duì)象組成的集合,這些對(duì)象描述了客觀(guān)世界中的真實(shí)個(gè)體;關(guān)聯(lián)是兩個(gè)類(lèi)對(duì)象之間的二元關(guān)系,描述的是客觀(guān)世界中的個(gè)體之間的聯(lián)系。從數(shù)學(xué)集合的角度看,如果將類(lèi)看成集合,將類(lèi)對(duì)象看成集合中的元素,那么關(guān)聯(lián)就是兩個(gè)集合的元素之間的二元組。由于這些二元組描述的是同一類(lèi)的二元關(guān)系,因此它們也組成了關(guān)聯(lián)的集合。
在鄰居關(guān)聯(lián)中,將Person、neighbour都看作集合,用p表示Person集合中的元素,用pn1表示Person集合中扮演neighbour角色的元素,pn2表示Person集合中另一個(gè)扮演neighbour角色的元素,則Person集合可以表示為公式(1)。
IsNeighbour關(guān)系中只存在neighbour一個(gè)角色。neighbour集合中的元素都是Person集合中的一個(gè)元素與Person集合中的另一個(gè)元素組合成的二元組,表示為公式(2)。
公式(2)表示的 neighbour集合中,二元組<pn1,pn2>表示pn2在當(dāng)前關(guān)聯(lián)中所扮演的角色是neighbour,從相反的導(dǎo)航方向來(lái)看,pn1在當(dāng)前關(guān)聯(lián)中所扮演的角色也是 neighbour,因此<pn1,pn2>與<pn2,pn1>等價(jià),也就是說(shuō)二元關(guān)系neighbour與其自身的逆等價(jià),表示為公式(3)。
如果把Person集合中的每個(gè)元素看成頂點(diǎn),把元素之間的關(guān)系看成頂點(diǎn)與頂點(diǎn)之間的邊,把Person集合中的每個(gè)元素看成頂點(diǎn)與頂點(diǎn)之間的關(guān)聯(lián)函數(shù),就可以將對(duì)稱(chēng)一元關(guān)聯(lián)轉(zhuǎn)化為圖,如圖2所示。根據(jù)關(guān)聯(lián)“IsNeighbour”的語(yǔ)義,由于對(duì)稱(chēng)一元關(guān)聯(lián)具有對(duì)等性和相互性,因此該圖是一個(gè)無(wú)向圖[10]。
圖2 用圖表示“鄰居”關(guān)系
其中,pn(n=0,1,2,3,4,5,6,7,8)是Person集合中的元素,其中 p0、p1、p2、p3、p4兩兩之間互為鄰居,p4、p5、p6、p7兩兩之間互為鄰居,p7和 p8互為鄰居。如果用G表示這個(gè)圖,用V(G)表示圖G中的所有頂點(diǎn)的集合,用E(G)表示圖G中的頂點(diǎn)之間邊的集合,用φ(G)表示頂點(diǎn)與頂點(diǎn)之間的關(guān)聯(lián)函數(shù),則該圖記為:
φ(G)=E→V×V,且φ(i0)=(p0,p2),φ(i1)=(p0,p1),φ(i2)=(p0,p3)
綜上所述,對(duì)稱(chēng)一元關(guān)聯(lián)的特點(diǎn)是關(guān)聯(lián)雙方所扮演的角色是相同的且具有相互性。正是由于這個(gè)特點(diǎn),當(dāng)用UML表示對(duì)稱(chēng)一元關(guān)聯(lián)時(shí),無(wú)法區(qū)分兩個(gè)相同的角色。對(duì)此,可以對(duì)UML元模型進(jìn)行擴(kuò)展,使之支持對(duì)稱(chēng)一元關(guān)聯(lián)的建模。
對(duì)稱(chēng)一元關(guān)聯(lián)語(yǔ)義的特殊性導(dǎo)致了在生成代碼時(shí)出現(xiàn)錯(cuò)誤代碼,下面將分析實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)的幾種數(shù)據(jù)結(jié)構(gòu),為生成實(shí)體類(lèi)代碼提供基礎(chǔ)。
根據(jù)上一節(jié)所討論的語(yǔ)義,可以將現(xiàn)實(shí)生活中的對(duì)稱(chēng)一元關(guān)聯(lián)抽象成一個(gè)無(wú)向圖。一個(gè)圖所包含的信息由兩個(gè)方面,一是圖中頂點(diǎn)的信息,二是頂點(diǎn)與頂點(diǎn)之間關(guān)系的信息,即邊的信息。無(wú)論采用什么數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)圖,都要完整、準(zhǔn)確地反映這兩方面的信息[11]。除了文獻(xiàn)[2]中使用的集合類(lèi)型以外,常用的存儲(chǔ)無(wú)向圖的數(shù)據(jù)結(jié)構(gòu)有鄰接矩陣、鄰接表、鄰接多重表。
(1)鄰接矩陣
鄰接矩陣存儲(chǔ)圖的方法是用一個(gè)一維數(shù)組存放圖中的頂點(diǎn)的信息,用一個(gè)二維數(shù)組存放圖中邊的信息,這個(gè)二維數(shù)組也稱(chēng)為鄰接矩陣。如圖2的無(wú)向圖有九個(gè)頂點(diǎn),則一維數(shù)組的長(zhǎng)度為9,如公式(8)所示,其鄰接矩陣是一個(gè)9×9的方陣,由于該圖是一個(gè)無(wú)環(huán)無(wú)向圖,所以方陣中主對(duì)角線(xiàn)上的各個(gè)位置的值都為0,其余位置的值根據(jù)兩個(gè)頂點(diǎn)之間是否有邊而定,若有邊則值為1,若無(wú)邊則為0,如公式(9)所示。
從公式(9)中不難看出,在 p0、p1、p2、p3、p4五個(gè)頂點(diǎn)構(gòu)成的五階子式中,每?jī)蓚€(gè)不同頂點(diǎn)之間的邊的信息都被重復(fù)存儲(chǔ)了一次,p4、p5、p6、p7四個(gè)頂點(diǎn)構(gòu)成的四階子式和p7、p8兩個(gè)頂點(diǎn)構(gòu)成的二階子式同理,而根據(jù)對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義,被重復(fù)存儲(chǔ)的兩個(gè)邊并無(wú)區(qū)別,只需存儲(chǔ)一次,因此會(huì)出現(xiàn)數(shù)據(jù)的冗余。
(2)鄰接表
鄰接表存儲(chǔ)圖的方法是將每個(gè)頂點(diǎn)的所有鄰接點(diǎn)鏈接成一個(gè)單鏈表,稱(chēng)為頂點(diǎn)的邊表。圖2的無(wú)向圖的鄰接表存儲(chǔ)示意圖如圖3所示。
圖3 圖2的無(wú)向圖的鄰接表存儲(chǔ)示意圖
從圖3中可以看出,鄰接表與鄰接矩陣一樣,也會(huì)出現(xiàn)兩個(gè)不同頂點(diǎn)之間的邊的信息被重復(fù)存儲(chǔ)的情況。
(3)鄰接多重表
鄰接多重表主要用于存儲(chǔ)無(wú)向圖,其存儲(chǔ)圖的方法與鄰接表類(lèi)似,但在鄰接表的基礎(chǔ)上針對(duì)無(wú)向圖的邊重復(fù)存儲(chǔ)的問(wèn)題做了改進(jìn)。圖2的無(wú)向圖的部分頂點(diǎn)的鄰接多重表存儲(chǔ)示意圖如圖4所示。
圖4 圖2的無(wú)向圖的鄰接多重表存儲(chǔ)示意圖
從圖4中可以看出,兩個(gè)不同頂點(diǎn)之間的邊的信息只存儲(chǔ)了一次,便于邊的查找和刪除。
鄰接矩陣便于確定兩個(gè)頂點(diǎn)之間是否有邊,但時(shí)間代價(jià)較高,在存儲(chǔ)無(wú)向邊時(shí)存在邊信息的冗余,在處理稠密圖時(shí),空間效率較高。鄰接表的時(shí)間代價(jià)較低,但在存儲(chǔ)無(wú)向邊時(shí)同樣存在邊信息的冗余,在處理稀疏圖時(shí),空間效率較高。鄰接多重表的時(shí)間代價(jià)較低,便于邊的查找和刪除,在存儲(chǔ)無(wú)向邊時(shí)不存在邊信息的冗余,但是其結(jié)構(gòu)比前兩種復(fù)雜,適用于查找和刪除操作較頻繁的無(wú)向圖。表1對(duì)比了這三種圖的數(shù)據(jù)結(jié)構(gòu)的優(yōu)缺點(diǎn)。
表1 鄰接矩陣、鄰接表、鄰接多重表的對(duì)比
無(wú)向圖的鄰接矩陣、鄰接表、鄰接多重表的存儲(chǔ)各有利弊,在具體實(shí)現(xiàn)時(shí),不但要根據(jù)圖的稀疏和稠密程度選擇合適的存儲(chǔ)方式,而且要考慮待解決問(wèn)題的特殊需求。
通過(guò)前文的討論可知,UML元模型不支持對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義表示,需要對(duì)UML元模型進(jìn)行擴(kuò)展。本節(jié)將討論通過(guò)擴(kuò)展類(lèi)圖元模型的元屬性和約束使UML支持對(duì)稱(chēng)一元關(guān)聯(lián)建模的方法。
對(duì)象約束語(yǔ)言(Object Constraint Language,OCL)是一種用于描述UML模型約束的形式化語(yǔ)言[12],與傳統(tǒng)形式化語(yǔ)言相比,對(duì)象約束語(yǔ)言具有良好的易讀性、易寫(xiě)性和易理解性。本文通過(guò)為Association元元素添加元屬性和為Property添加對(duì)象約束的方法[13],對(duì)類(lèi)圖元模型進(jìn)行擴(kuò)展,使之支持對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義。首先,為了標(biāo)識(shí)模型中關(guān)聯(lián)是否是對(duì)稱(chēng)一元關(guān)聯(lián),在UML標(biāo)準(zhǔn)的基礎(chǔ)上,擴(kuò)展UML類(lèi)圖元模型中的Association元元素,在A(yíng)ssociation元元素中添加一個(gè)元屬性,該元屬性的信息如表2。
表2 擴(kuò)展的isSymmetricUnary屬性的信息
由于對(duì)稱(chēng)一元關(guān)聯(lián)的建模問(wèn)題源于關(guān)聯(lián)兩端的角色,而關(guān)聯(lián)端的角色是Property元元素的實(shí)例對(duì)象,因此須在Property元元素中對(duì)Association元元素的is-SymmetricUnary元屬性添加約束,用OCL描述該約束如下。
上述約束是對(duì)Property元元素的對(duì)象的約束,首先判斷當(dāng)前Property對(duì)象所在的關(guān)聯(lián)是否有兩個(gè)關(guān)聯(lián)端,如果是,則定義一個(gè)名為otherEnd的變量,用來(lái)表示關(guān)聯(lián)的另一端,然后給關(guān)聯(lián)的isSymmetricUnary屬性賦值。如果兩個(gè)關(guān)聯(lián)端的名稱(chēng)、數(shù)據(jù)類(lèi)型(即關(guān)聯(lián)兩端所連接的類(lèi))、可導(dǎo)航性、多重性的上界和下屆之中有一項(xiàng)不同,則isSymmetricUnary屬性的值為false,否則isSymmetricUnary屬性的值為true。在建模時(shí),計(jì)算機(jī)根據(jù)該約束自動(dòng)判斷關(guān)聯(lián)的類(lèi)型并給isSymmetricU-nary屬性賦值。
此外,在UML中,Property元元素間接繼承了NamedElement元元素,子類(lèi)可以根據(jù)需要重寫(xiě)父類(lèi)的方法,因此可以在Property元元素中重寫(xiě)NamedElement元元素的isDistinguishableFrom()方法,用于約束對(duì)稱(chēng)一元關(guān)聯(lián)中的角色共存。當(dāng)Association元元素的is-SymmetricUnary元屬性值為true時(shí),關(guān)聯(lián)兩端相同的角色可以在當(dāng)前命名空間中邏輯共存。用OCL描述重寫(xiě)的isDistinguishableFrom()方法如下。
上述方法同樣是對(duì)Property元元素的對(duì)象的約束,在NamedElement元元素的isDistinguishableFrom()方法的基礎(chǔ)上,添加了處理對(duì)稱(chēng)一元關(guān)聯(lián)的兩個(gè)相同關(guān)聯(lián)端的共存問(wèn)題的分支,當(dāng)Property對(duì)象所在的關(guān)聯(lián)的isSymmetricUnary屬性值為true時(shí),關(guān)聯(lián)兩端相同的角色可以在當(dāng)前命名空間中邏輯共存。對(duì)于非對(duì)稱(chēng)一元關(guān)聯(lián)的情況,若兩個(gè)關(guān)聯(lián)端具有相同的類(lèi)型,則二者是否可以共存取決于二者的名稱(chēng)是否相同:如果名稱(chēng)相同,則二者不可以共存,否則可以共存;若兩個(gè)關(guān)聯(lián)端具有不同的類(lèi)型,則二者可以共存。
為了方便建模人員在建模時(shí)根據(jù)需求為對(duì)稱(chēng)一元關(guān)聯(lián)選定合適的實(shí)現(xiàn)方式,可以在類(lèi)圖元模型的Association元元素中標(biāo)記對(duì)稱(chēng)一元關(guān)聯(lián)的實(shí)現(xiàn)方式,在生成代碼時(shí),根據(jù)不同的標(biāo)記值生成不同的代碼。首先定義要擴(kuò)展的元屬性的枚舉值,在UML標(biāo)準(zhǔn)的基礎(chǔ)上,在類(lèi)圖元模型中添加一個(gè)名為“SUAImplementation”的枚舉型數(shù)據(jù)類(lèi)型,這個(gè)數(shù)據(jù)類(lèi)型的值有“CollectionType”、“AdjacencyMatrix”、“AdjacencyList”、“AdjacencyMultiList”,分別表示集合類(lèi)型、鄰接矩陣、鄰接表、鄰接多重表;然后在類(lèi)圖元模型的Association元元素中添加一個(gè)元屬性,該元屬性的信息如表3。
表3 擴(kuò)展的implementation屬性的信息
需要注意的是,只有當(dāng)isSymmetricUnary元屬性的值是true時(shí),implementation元屬性才會(huì)啟用,否則其值為空。
圖5展示了擴(kuò)展后的UML類(lèi)圖元模型。
圖5 擴(kuò)展UML類(lèi)圖元模型
通過(guò)擴(kuò)展Association元元素的元屬性以及Property元元素的約束,從元模型層面使UML支持對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義,為建模工具實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)的建模提供了理論基礎(chǔ)。
從模型生成代碼是MDA思想中模型驅(qū)動(dòng)開(kāi)發(fā)的重要過(guò)程。在應(yīng)用程序的三層架構(gòu)中,領(lǐng)域?qū)ο笞鳛閿?shù)據(jù)的載體,扮演著關(guān)鍵的角色,領(lǐng)域?qū)ο笫菍?shí)體類(lèi)的實(shí)例,因此實(shí)體類(lèi)的代碼生成尤為重要。常用的代碼生成方法是基于模板的代碼生成方法。使用模板的好處是模板與數(shù)據(jù)分離,當(dāng)需要改動(dòng)代碼時(shí),只需修改模板,易于復(fù)用[14]。模板中可變的部分需要用模型的信息進(jìn)行替換,不可變的部分則是代碼中固定不變的語(yǔ)法格式。
除了文獻(xiàn)[2]中給出的代碼生成方法,本文的2.3節(jié)還討論了三種實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu),下面將給出這些實(shí)現(xiàn)方式的代碼生成算法,其中被“%”包裹的部分為需替換成元模型的元元素中的元屬性的值。
對(duì)稱(chēng)一元關(guān)聯(lián)實(shí)體類(lèi)代碼生成算法
輸入:UML類(lèi)圖的模型信息
輸出:對(duì)稱(chēng)一元關(guān)聯(lián)實(shí)體類(lèi)代碼
上述代碼首先獲取當(dāng)前類(lèi)圖的模型信息,循環(huán)判斷模型中的每個(gè)關(guān)聯(lián),如果關(guān)聯(lián)滿(mǎn)足對(duì)稱(chēng)一元關(guān)聯(lián)的條件,則按照實(shí)體類(lèi)代碼的模板框架生成類(lèi)的定義代碼,先在類(lèi)中循環(huán)生成該類(lèi)的成員屬性,然后判斷implementation屬性的值,生成該值所標(biāo)識(shí)的數(shù)據(jù)結(jié)構(gòu)的代碼。
鄰里社交平臺(tái)是一個(gè)專(zhuān)門(mén)為促進(jìn)小區(qū)內(nèi)鄰居之間的交流而開(kāi)發(fā)的平臺(tái),居民在該平臺(tái)上可以隨時(shí)查看生活圈,關(guān)注鄰居的最新動(dòng)態(tài),還可以向鄰居發(fā)出幫助請(qǐng)求,同時(shí)也可以幫助鄰居解決生活上遇到的難題,滿(mǎn)足了鄰里之間的交流與溝通的需求。
本節(jié)將以鄰里社交平臺(tái)的開(kāi)發(fā)為例,采用本文提出的方法,使用PowerDesigner實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)的建模和代碼生成,驗(yàn)證本文研究的方法在實(shí)際軟件開(kāi)發(fā)過(guò)程中的可用性和有效性。
本節(jié)討論對(duì)稱(chēng)一元關(guān)聯(lián)的應(yīng)用,以“鄰居”關(guān)系為示例,建立了對(duì)稱(chēng)一元關(guān)聯(lián)的UML類(lèi)圖,然后在PowerDesigner中實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)數(shù)據(jù)結(jié)構(gòu)的選擇,然后根據(jù)上一節(jié)提出的算法生成代碼。其次,還將同樣的方法應(yīng)用到一般的關(guān)聯(lián)中,說(shuō)明方法的通用性。
(1)建立對(duì)稱(chēng)一元關(guān)聯(lián)的領(lǐng)域模型
鄰里社交平臺(tái)的主要業(yè)務(wù)是實(shí)現(xiàn)鄰居之間的信息共享和交流。其中的特殊點(diǎn)在于,“鄰居”關(guān)系屬于對(duì)稱(chēng)一元關(guān)聯(lián),在使用類(lèi)圖建立領(lǐng)域模型時(shí),需用到本文提出的對(duì)稱(chēng)一元關(guān)聯(lián)的建模方法。其領(lǐng)域模型如圖6所示。
圖6 鄰里社交平臺(tái)核心業(yè)務(wù)的領(lǐng)域模型
領(lǐng)域模型中Person類(lèi)表示使用系統(tǒng)的用戶(hù),用戶(hù)與用戶(hù)之間的鄰居關(guān)系通過(guò)名為“IsNeighbour”的關(guān)聯(lián)來(lái)表示,該關(guān)聯(lián)雙方的鄰居角色用名為neighbour的角色表示,且多重性的值為0...*,兩端都是可導(dǎo)航端。根據(jù)前文中提出的UML元模型的擴(kuò)展約束,該領(lǐng)域模型中IsNeighbour關(guān)聯(lián)滿(mǎn)足對(duì)稱(chēng)一元關(guān)聯(lián)的條件,因此該關(guān)聯(lián)的isSymmetricUnary屬性值為true,進(jìn)而關(guān)聯(lián)兩端相同的neighbour角色可以在當(dāng)前命名空間中邏輯共存。
(2)數(shù)據(jù)結(jié)構(gòu)的選擇和代碼生成
為了方便建模人員在生成代碼時(shí)選擇合適的數(shù)據(jù)結(jié)構(gòu),使用PowerDesigner提供的Profile擴(kuò)展功能,在資源編輯器中將一個(gè)用于標(biāo)識(shí)對(duì)稱(chēng)一元關(guān)聯(lián)實(shí)現(xiàn)方式的擴(kuò)展屬性添加到類(lèi)圖的Association元元素下,并給定這個(gè)擴(kuò)展屬性的四個(gè)可選值,分別表示使用集合類(lèi)型、鄰接矩陣、鄰接表、鄰接多重表,然后將四種實(shí)現(xiàn)方式的代碼生成算法應(yīng)用到這個(gè)擴(kuò)展屬性中,最后提供建模人員的操作界面,如圖7所示。
圖7 在PowerDesigner中選擇代碼生成的方式
通過(guò)擴(kuò)展PowerDesigner的操作界面,建模人員在建模時(shí)可以在類(lèi)圖的Association元素的Properties窗口下選擇合適的數(shù)據(jù)結(jié)構(gòu)以標(biāo)記使用哪種方式來(lái)生成對(duì)稱(chēng)一元關(guān)聯(lián)的代碼。當(dāng)然,這四個(gè)選項(xiàng)可用的前提是當(dāng)前的關(guān)聯(lián)滿(mǎn)足對(duì)稱(chēng)一元關(guān)聯(lián)的條件。如圖7所示,此處選擇鄰接多重表來(lái)實(shí)現(xiàn)“鄰居”對(duì)稱(chēng)一元關(guān)聯(lián),生成的Java代碼如下。
在圖6所示的領(lǐng)域模型中,Reply關(guān)聯(lián)描述了用戶(hù)與消息之間的“回復(fù)”關(guān)系,一個(gè)用戶(hù)可以回復(fù)多條消息,一條消息可以被多個(gè)用戶(hù)回復(fù)。用戶(hù)的角色用名為Replier的角色表示,消息的角色用名為ReplyMessage的角色表示,關(guān)聯(lián)兩端的多重性的值都為0...*且都為不可導(dǎo)航端。
雖然Reply關(guān)聯(lián)兩端的角色的多重性、可導(dǎo)航性相同,但類(lèi)型、角色名都不相同,不滿(mǎn)足對(duì)稱(chēng)一元關(guān)聯(lián)的條件,因此該關(guān)聯(lián)的isSymmetricUnary屬性值為假,關(guān)聯(lián)兩端的角色可以在當(dāng)前命名空間中邏輯共存。在本文的討論范圍內(nèi),對(duì)于非對(duì)稱(chēng)一元關(guān)聯(lián),無(wú)法選擇對(duì)稱(chēng)一元關(guān)聯(lián)的實(shí)現(xiàn)方式,SUAImplementation屬性被禁用,如圖8所示。
圖8 “Reply”關(guān)聯(lián)的SUAImplementation屬性被禁用
因?yàn)镽eply關(guān)聯(lián)不屬于對(duì)稱(chēng)一元關(guān)聯(lián),所以在生成代碼時(shí)使用的是傳統(tǒng)的代碼生成的算法,將Replier角色作為成員屬性生成到Message類(lèi)中,將ReplyMessage角色作為成員屬性生成到Person類(lèi)中,由于兩端的多重性值都為0...*,所以數(shù)據(jù)類(lèi)型為數(shù)組,生成的Java代碼如下。
文獻(xiàn)[15]針對(duì)UML存在組合關(guān)聯(lián)語(yǔ)義定義模糊,導(dǎo)致從領(lǐng)域模型自動(dòng)轉(zhuǎn)換為數(shù)據(jù)訪(fǎng)問(wèn)層代碼的精確度不足的問(wèn)題,提出了描述邏輯CATSbqr語(yǔ)言,并準(zhǔn)確地描述了UML組合關(guān)聯(lián)的語(yǔ)義以及動(dòng)態(tài)操作復(fù)雜對(duì)象數(shù)據(jù)的語(yǔ)義,并通過(guò)實(shí)例說(shuō)明了該方法的實(shí)用性,為實(shí)現(xiàn)領(lǐng)域模型到數(shù)據(jù)訪(fǎng)問(wèn)層的代碼自動(dòng)化提供了理論基礎(chǔ)。
文獻(xiàn)[2]針對(duì)UML無(wú)法表示對(duì)稱(chēng)一元關(guān)聯(lián)的問(wèn)題,提出使用ConML元模型中的半關(guān)聯(lián)(SemiAssociation)元元素,將對(duì)稱(chēng)一元關(guān)聯(lián)分解成一個(gè)主半關(guān)聯(lián)(Primary)和一個(gè)次半關(guān)聯(lián)(Secondary),且允許關(guān)聯(lián)兩端的角色相同,并使用Microsoft Visual Studio Enterprise 2015、Visual Paradigm 14.0、ArgoUML 0.34和 XCode 8.0這四種代碼生成工具生成對(duì)稱(chēng)一元關(guān)聯(lián)模型的代碼,通過(guò)表達(dá)語(yǔ)義的精確與否、是否有冗余、是否能編譯、是否無(wú)需開(kāi)發(fā)者編輯等指標(biāo)對(duì)比四種工具生成代碼的質(zhì)量,得出的結(jié)論是為了能生成滿(mǎn)足對(duì)稱(chēng)一元關(guān)聯(lián)語(yǔ)義的代碼,最優(yōu)的解決方案是修改底層元模型和代碼生成規(guī)則,將這些規(guī)則運(yùn)用到代碼生成工具中,從而實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)。
與上述關(guān)于UML關(guān)聯(lián)的研究工作相比,本文針對(duì)客觀(guān)世界普遍存在但UML無(wú)法支持的對(duì)稱(chēng)一元關(guān)聯(lián)進(jìn)行了重點(diǎn)討論,用集合、圖等數(shù)學(xué)方法分析了對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義,通過(guò)擴(kuò)展UML類(lèi)圖元模型實(shí)現(xiàn)語(yǔ)義的支持,并討論了對(duì)稱(chēng)一元關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu)和代碼生成算法,提高了UML的建模能力,并且實(shí)現(xiàn)根據(jù)需求生成更高效、更少錯(cuò)誤的數(shù)據(jù)訪(fǎng)問(wèn)代碼。
UML元模型定義了模型元素在同一個(gè)命名空間中共存的約束,使UML類(lèi)圖元模型無(wú)法表示對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義。由于對(duì)稱(chēng)一元關(guān)聯(lián)的特殊語(yǔ)義,在從類(lèi)圖生成代碼時(shí),會(huì)導(dǎo)致語(yǔ)義的缺失,從而生成錯(cuò)誤的代碼。本文針對(duì)以上兩個(gè)問(wèn)題,使用集合、圖等數(shù)學(xué)方法分析了對(duì)稱(chēng)一元關(guān)聯(lián)的語(yǔ)義,討論了集合、鄰接矩陣、鄰接表、鄰接多重表四種可用于對(duì)稱(chēng)一元關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu),提出了通過(guò)擴(kuò)展UML類(lèi)圖元模型使UML支持對(duì)稱(chēng)一元關(guān)聯(lián)建模的方法和代碼生成算法,最后通過(guò)實(shí)際例子的應(yīng)用驗(yàn)證了該方法的可用性和有效性,為實(shí)現(xiàn)對(duì)稱(chēng)一元關(guān)聯(lián)的建模及數(shù)據(jù)訪(fǎng)問(wèn)代碼的自動(dòng)化提供了理論基礎(chǔ)。