UML軟件開發(fā)與建模工具Enterprise Architect教程:創(chuàng)建項(xiàng)目特定的代碼生成器(上)
Enterprise Architect是一個(gè)對(duì)于軟件系統(tǒng)開發(fā)有著極好支持的CASE軟件(Computer Aided Software Engineering)。EA不同于普通的UML畫圖工具(如VISIO),它將支撐系統(tǒng)開發(fā)的全過程。在需求分析階段,系統(tǒng)分析與設(shè)計(jì)階段,系統(tǒng)開發(fā) 及部署等方面有著強(qiáng)大的支持,同時(shí)加上對(duì)10種編程語言的正反向工程,項(xiàng)目管理,文檔生成,數(shù)據(jù)建模等方面。可以讓系統(tǒng)開發(fā)中各個(gè)角色都獲得最好的開發(fā)效率。
點(diǎn)擊下載Enterprise Architect最新試用版
您是否曾經(jīng)想過從Enterprise Architect UML或SysML模型生成代碼?您是否嘗試過自定義Enterprise Architect的代碼模板框架?不要放棄特定于項(xiàng)目的代碼生成器的夢想,而要閱讀它們實(shí)現(xiàn)起來的難易程度。
需要代碼生成器
與實(shí)現(xiàn)相比,好的軟件或系統(tǒng)架構(gòu)具有更高的抽象級(jí)別。它應(yīng)該是一個(gè)一致的模型,用于記錄決策并忽略不必要的(通常是技術(shù)上的)細(xì)節(jié)。考慮一下圖1中的類圖。它顯示了一個(gè)域模型,該模型定義了商店允許客戶訂購商品所需的數(shù)據(jù)結(jié)構(gòu)。每個(gè)類的屬性都進(jìn)行了詳細(xì)建模,但是省略了其他不必要的方面,例如訪問屬性的操作。
圖1:示例UML模型
如果在開始實(shí)施之前先對(duì)軟件體系結(jié)構(gòu)/設(shè)計(jì)進(jìn)行了準(zhǔn)備,那么代碼生成可以避免很多繁瑣且容易出錯(cuò)的工作。商業(yè)開箱即用的代碼生成器通常不會(huì)更改抽象程度。這就是為什么它們通常不符合項(xiàng)目需求的原因。
可以根據(jù)項(xiàng)目特定需求定制Enterprise Architect的代碼模板框架。但這需要一些初步培訓(xùn)。通常,如基于Eclipse的Enterprise Architect Models的代碼生成中所述,很難達(dá)到預(yù)期的結(jié)果。
一個(gè)簡單的項(xiàng)目專用代碼生成器
我更喜歡使用Java或Xtend之類的通用編程語言來實(shí)現(xiàn)代碼生成器。由于Xtend具有模板表達(dá),因此特別適合于實(shí)現(xiàn)模板。它們?cè)试S將可執(zhí)行代碼嵌入要生成的文本中。感覺就像在編程PHP,JSP或JSX。在代碼清單1只顯示了寫的Xtend代碼生成模板。它為圖1的類圖中聲明的類生成Java類。
package com.yakindu.ea.examples.orderingsoftware.template import com.yakindu.bridges.ea.examples.runtime.codegen.EACodegen import org.eclipse.uml2.uml.Class import org.eclipse.uml2.uml.NamedElement class ClassTemplate { @EACodegen("Java") def String generate(Class element) ''' package ?element.package.javaQualifiedName?; ?val superType = element.generals.findFirst[true]?.name? ?val extends = '''?IF !superType.isNullOrEmpty? extends ?superType??ENDIF?'''? public?IF element.isAbstract? abstract?ENDIF? class ?element.name??extends? { ?FOR attribute : element.ownedAttributes SEPARATOR System.lineSeparator? ?val type = attribute.type?.javaQualifiedName? ?IF 1 != attribute.upper? ?val defaultValue = '''new java.util.LinkedList<?type?>()'''? private final java.util.List<?type?> ?attribute.name? = ?defaultValue?; ?ELSE? private ?type? ?attribute.name?; ?ENDIF? ?ENDFOR? ?FOR attribute : element.ownedAttributes SEPARATOR System.lineSeparator? ?val type = attribute.type?.javaQualifiedName? ?IF 1 != attribute.upper? public List<?type?> get?attribute.name.toFirstUpper?() { return ?attribute.name?; } ?ELSE? public ?type? get?attribute.name.toFirstUpper?() { return ?attribute.name?; } ?ENDIF? ?IF 1 == attribute.upper? ?val params = '''?type? ?attribute.name?'''? public void set?attribute.name.toFirstUpper?(?params?) { this.?attribute.name? = ?attribute.name?; } ?ENDIF? ?ENDFOR? } ''' protected def String getJavaQualifiedName(NamedElement element) { element.qualifiedName.replace("::", ".") } }
清單1:用Xtend編寫的示例代碼生成模板
在顯示生成的Java代碼清單2, 3 和 4看起來并不像手寫的,因?yàn)楹细竦拿Q來代替進(jìn)口。稍后將在圖4中通過方法collectImports和進(jìn)行改進(jìn)printImports。
package com.example.orderingsoftware; public abstract class AbstractIDObject { private java.util.UUID id; public java.util.UUID getId() { return id; } public void setId(java.util.UUID id) { this.id = id; } }
清單2:清單1中的代碼生成模板生成的類AbstractIDObject的Java代碼
package com.example.orderingsoftware; public class OrderItem extends AbstractIDObject { private java.math.BigInteger amount; private com.example.orderingsoftware.Article article; public java.math.BigInteger getAmount() { return amount; } public void setAmount(java.math.BigInteger amount) { this.amount = amount; } public com.example.orderingsoftware.Article getArticle() { return article; } public void setArticle(com.example.orderingsoftware.Article article) { this.article = article; } }
package com.example.orderingsoftware; public class Order extends AbstractIDObject { private java.util.Date date; private com.example.orderingsoftware.Customer customer; private final java.util.List<OrderItem> items = new java.util.LinkedList<OrderItem>(); public java.util.Date getDate() { return date; } public void setDate(java.util.Date date) { this.date = date; } public com.example.orderingsoftware.Customer getCustomer() { return customer; } public void setCustomer(com.example.orderingsoftware.Customer customer) { this.customer = customer; } public java.util.List<com.example.orderingsoftware.OrderItem> getItems() { return items; } }
清單4:由清單1中的代碼生成模板生成的Order類的Java代碼
如果仔細(xì)查看清單1中的模板,您將意識(shí)到它對(duì)Enterprise Architect一無所知。取而代之的是,它處理UML元模型的實(shí)例,這要?dú)w功于Eclipse UML 2項(xiàng)目,它在Eclipse中可用。YAKINDU EA-Bridge是Enterprise Architect和UML之間缺少的連接。它是一個(gè)API,可提供對(duì)Enterprise Architect UML和SysML模型的符合UML的讀寫權(quán)限。Enterprise Architect項(xiàng)目背后的數(shù)據(jù)庫會(huì)自動(dòng)轉(zhuǎn)換為UML元模型的實(shí)例。作為開發(fā)人員,這具有三大優(yōu)勢:
- 您的代碼與基于UML 2項(xiàng)目的其他工具(如Papyrus)兼容。
- 對(duì)Enterprise Architect模型的高性能讀寫訪問,而無需對(duì)Enterprise Architect的數(shù)據(jù)庫架構(gòu)進(jìn)行反向工程。
- 您無需了解有關(guān)YAKINDU EA-Bridge API的任何知識(shí)。作為開發(fā)人員,它完全是隱藏的,因?yàn)閅AKINDU EA-Bridge會(huì)將自身集成到Eclipse Modeling Framework(EMF)的生態(tài)系統(tǒng)中。
YAKINDU EA-Bridge帶有可選的Eclipse IDE集成,該集成允許一個(gè)人實(shí)現(xiàn)特定于項(xiàng)目的代碼生成器。這些代碼生成器通常是原型開發(fā)的,并且僅在特定上下文中執(zhí)行。因此,至關(guān)重要的是,與手動(dòng)編碼相比,減少開發(fā)工作量。要實(shí)現(xiàn)特定于項(xiàng)目的代碼生成器,您要做的就是將EAP文件放置在Eclipse項(xiàng)目中,并使用注釋代碼生成模板中的方法@EACodegen。帶注釋的方法應(yīng)接受應(yīng)為其生成代碼的UML元素作為唯一參數(shù),并返回生成的文本。如果您的Enterprise Architect模型由遠(yuǎn)程數(shù)據(jù)庫(例如Microsoft SQL Server)托管,則可以使用快捷方式文件 而不是EAP文件。
例如,通過主菜單項(xiàng)“ Project,Clean ...”自動(dòng)或手動(dòng)構(gòu)建項(xiàng)目時(shí),將為所有EAP文件中聲明的所有UML類啟動(dòng)模板。當(dāng)然,僅考慮模板項(xiàng)目中存儲(chǔ)的EAP文件。生成的代碼保存在類的限定名稱指定的文件中。文件擴(kuò)展名由@EACodegen注釋的參數(shù)指定。Eclipse項(xiàng)目的結(jié)構(gòu)如圖2所示。

圖2:Eclipse中的示例項(xiàng)目結(jié)構(gòu)
請(qǐng)注意,YAKINDU EA-Bridge是一個(gè)API。它允許您以任何方式處理Enterprise Architect模型。實(shí)際上,最初的用例是全面的代碼生成器,例如基于UML架構(gòu)的Autosar RTE生成器。
=====================================================
想要了解或購買Enterprise Architect正版版權(quán),請(qǐng)
關(guān)注下方微信公眾號(hào),及時(shí)獲取產(chǎn)品最新消息和最新資訊
