EMF 是 Eclipse 平台的主要部分,並且是一些相關技術和框架的基礎,比如 Eclipse Visual Editor、SDO、XSD 和 UML — 其中的許多技術都被集成到 Rational® Application Developer 和 WebSphere® Business Modeler 等 IBM® 平台中。現在,EMF 已經吸收了許多 Java 技術特性,比如枚舉類型、注釋和泛型。
在大多數文檔和教程中,EMF 都被用於建模數據 和接口(比如 EMF 發行文檔中的 Library 和 Books),而不用於建模行為。當然,還有一些針對數據對象生成的默認方法實現,但這些實現涉及到模型元素之間的關系。而且,將 EMF 用作 “元模型” 的經過歸檔的示例非常少 — 除了 Eclipse Foundation 文章 “Modeling Rule-Based Systems with EMF”— 但是這個示例並沒有展示如何擴展 Ecore 元模型。
最後,使用和擴展 EMF JET 模板的過程也沒有被很好地進行歸檔。此外,JET Editor 項目最近已經遷移到另一個 Eclipse 項目(M2T)上。本文旨在澄清這些問題,並使您能夠在 EMF 上下文中使用動態模板實現更多的功能。因此,本文假設您對 EMF 有基本的了解。
為什麼要擴展 Ecore 元模型?
Ecore 究竟是什麼?
Eclipse Modeling Framework (EMF) 是 Eclipse 的一個建模框架。根據 Eclipse Foundation 的定義,核心 EMF 框架包括一個描述模型的元模型(Ecore)和模型的運行時支持,包括更改通知、對默認 XMI 序列化的持久性支持和用於對 EMF 對象執行常規操作的反射 API(reflective API)。換句話說,Ecore 定義核心模型的結構,而核心模型定義開發人員用於維護應用程序數據的模型結構。
Ecore 元模型是一個強大的工具,可用於設計模型驅動架構(Model-Driven Architecture,MDA),後者可以作為軟件開發的起點。通常情況下,我們定義應用程序范圍內的對象(EClass 類型)、對象屬性以及它們之間的關系。我們還使用 EOperation 模型元素定義屬於這些對象的特定操作。默認情況下,EMF 將會為這些操作生成骨架 或方法簽名,但是我們必須返回並實現這些操作,常常要反復地重新編寫類似的邏輯。
但是,如果我們想在模型中指定某種任意的實現行為該怎麼辦呢?一種方法是添加基於文本的注釋(EAnnotation 類型),以建模對象並在代碼生成期間解釋模板中的這些注釋。關於這種方法的出色示例,可以查閱 Eclipse Foundation 文章 “Implementing Model Integrity in EMF with MDT OCL”。但是,正如這篇文章中所描述的,我們的目標不是驗證模型元素,而是對實現本身進行建模,以使任何具體的模型能夠重用這些元模型元素。為此,我們需要擴展 Ecore 元模型。
擴展了的元模型
本文附帶了一個高度簡化的用來擴展 Ecore 的編程式模型。它不是一個完整或連貫的元模型或框架;嚴格來講,它是一個元素的原型集合,用於演示使用 EMF 對代碼實現進行元建模的能力。圖 1 顯示了我們的擴展元模型示例 EcoreX 的快照,下面是每個元素的簡短描述。
圖 1. EcoreX 模型
EcoreX 元素
EPackageX 擴展 EPackage
這是 Ecore 元素 EPackage 的一個簡單 “標記” 擴展,沒有任何附加屬性。這個元素是必需的,因為在默認情況下,元素 EPackage EMF 編輯器插件不允許將 EClass 的子類作為子元素添加(參閱下面的 EClassX)。通過提供一個可擴展 EPackage 的模型元素,代碼將會自動生成,從而允許將一個 EClassX 子元素添加到 EPackageX 中。
EClassX 擴展 EClass
同樣地,這是 Ecore 元素 EClass 的一個簡單標記擴展,沒有任何附加屬性。與上面的元素類似,此元素也是必需的,因為在默認情況下,EClass 的編輯器插件不允許添加 EOperation 的子類 — 這正是我們要在本文中實現的目標。
EOperationImpl 擴展 EOperation
這是用於向 Ecore 模型添加具體的元功能的基本實體和入口點。此元素被賦予 Ecore 的基礎 EOperation 元素中沒有的屬性。下面描述的所有其他元素都屬於 EOperationImpl 並用於構成編程式實現。例如,EOperationImpl 包含變量和語句,可以返回一個引用或值。
LocalVariable 擴展 ETypedElement
LocalVariable
是一個本地變量。變量包含一個名稱和一個 Java 類型(比如 String、Integer、Object),而且由於這些屬性已經存在於其超級超類(super-superclass)EParameter 中,所以 LocalVariable 不需要額外屬性。
Statement 擴展 EClass
在我們的簡化邏輯模型中,一個 EOperationImpl 包含許多將會按給定順序計算的 statement。Statement 是一個抽象超類。
LiteralAssignment 擴展 Statement
引用一個變量,並且有一個 String 屬性,允許用戶輸入一個要被解析的值並將其分配給一個變量(例如,“hello”、“4.5” 可以分別分配給 String 或 float)。
LiteralAssignment
Access 擴展Statement
表示引用 Java 字段或操作的動作。
Access
FieldReferenceAssignment 擴展 Access
訪問一個字段,以分配一個值(例如,var1 = var2.name)。
Invoke 擴展 Access
調用一個操作(Java 方法)。Invoke 的結果可以分配給一個變量(例如,myVar = obj.toString())。
圖 2 展示了 EcoreX 元模型的一種更加類似 UML 的表示。
圖 2. Ecorex 模型圖
入門
本文包括六個高級的步驟:
擴展 Ecore 元模型,添加新語義
為被擴展的元模型創建一個 genmodel。
為此元模型生成一個 EMF 編輯器,並作為插件安裝。
使用這個新編輯器,構建一個具體的模型來描述編程行為。
為這個具體的模型創建並配置一個 genmodel。
基於這個具體的模型生成具體的 Java 代碼。
可以創建或導入上面描述的元模型。兩種情況都需要從一個現有 EMF 項目或創建一個新項目入手(New > Other > Eclipse Modeling Framework > Empty EMF Project)。我們的項目名為 EMFX,並且它應包含一個名為 model 的文件夾。可以將這個 EcoreX.ecore 模型復制到 model 目錄並跳至 構建和啟動 Editor Metamodel 插件 小節,也可以執行以下步驟,從頭創建一個元模型。
擴展 Ecore 元模型 — 從頭開始
右鍵單擊項目,從上下文菜單中選擇 New > Other > Example EMF Model Creation Wizards > Ecore Model。(對於 Eclipse V3.5+ [Galileo, Helios],則應選擇 New > Other > Eclipse Modeling Framework > Ecore Model。)選擇 model 文件夾和名稱 EcoreX.ecore。
默認情況下,我們將模型包稱為 ecorex。在模型窗口中右鍵單擊並選擇 Load Resource > Browse Registered Packages。選擇具有名稱空間 http://www.eclipse.org/emf/2002/Ecore 的 Ecore Model。
導入 Ecore 元模型之後,就可以對其進行擴展了。要重新創建 ecorex.ecore 模型,首先在包元素 ecorex 上右鍵單擊並選擇 New Child EClass。將此元素稱為 EPackageX(參閱上面的模型元素描述)。然後需要將基元素 EPackage 作為這個新元素的 ESuper Type 添加。
通過將 EClass 指定為 ESuperType,使用相同的過程創建新元素 EClassX。根據需要對 Ecore 對象劃分子類,在 EcoreX 模型中繼續定義其他 EClass。使用圖 1 和 EcoreX.ecore 文件了解要為哪個 EClass 創建什麼屬性。
構建並啟動 the Editor Metamodel 插件
I在構建步驟中,我們將創建元模型 genmodel 並構建模型和編輯器項目。右鍵單擊 EcoreX 項目並選擇 New > Other > Eclipse Modeling Framework > EMF Model。(對於 EMF V2.5+ [Galileo, Helios],則應選擇 New > Other > Eclipse Modeling Framework > EMF Generator Model。)可以提供一個名稱或接受默認的名稱 EcoreX.genmodel。EcoreX 模型應該被預選擇為 genmodel 的基模型。單擊 Load 驗證 EcoreX.ecore 元模型。
圖 3. 新 EMF 模型
當要求指定要生成和從其他生成器模型引用的包時,選擇 Root packages 下面的 EcoreX 包和 Referenced generator models 下面的 Ecore。
現在,向導將為元模型創建一個 genmodel。突出顯示 genmodel 中的頂級元素之後,從上下文菜單中選擇 Generate All,這樣可以自動生成關聯的代碼。根據在 genmodel 中配置的行為,這將生成 4 個 Eclipse 項目。本文不會關注 .test 項目,所以您可能不希望生成這個插件。
現在我們繼續啟動步驟。在大多數 Eclipse 教程中,都會要求您在單獨的 Eclipse 過程中啟動所開發的插件。在本節中,我們將采用一種不同的方法:我們將在當前 Eclipse 和工作區中激活插件。這樣更容易將預構建的元模型與下一節中具體的模型開發集成。為此:
雙擊 EMFX plugin.xml 打開插件配置編輯器。
單擊 Exporting 選項卡下的 Export Wizard。
選擇基本的建模插件和兩個編輯器插件。
在 Destination 選項卡下,選擇 Eclipse 安裝目錄,或托管存儲庫(如果可用)。
圖 4. 導出
注意:如果使用 –console 選項啟動 Eclipse,您可以使用 OSGi 命令控制台動態地啟動、停止、更新和刷新插件(插件組),無需重新啟動 Eclipse 或啟動一個單獨的實例。
單擊 Finish 時,會自動構建生成的插件 JAR 文件,並自動將其復制到插件目錄。此時,您需要重新啟動 Eclipse,激活新插件。現在我們已經准備好啟動編輯器插件了,創建一個新項目來保存我們的具體模型(我們的模型命名為 Test2)。
在這個新項目中,導航到 New > Other Example EMF Model Creation Wizards > Ecorex Model 並提供一個模型名稱。注意:在 EMF 的最新版本 (V2.5+) 中,具體模型的文件擴展名必須被設為 .ecore,而不是 .ecorex;否則,這個具體的 genmodel 將不能在後續步驟中被成功創建。選擇 EPackageX 元素。您現在有了一個空的具體模型。後續小節將討論如何構建這些編程模型元素;完成後的文件 My.ecore 可以在 參考資料 部分找到。
建模具體的測試模型
在本節中,我們將對一個具體的 Java 類(EClassX 的實例)進行建模,這個類包含兩個具體的方法,我們將對這兩個方法的實現進行建模。第一個示例方法接受 String 參數消息,並輸出消息和一個時間戳 — 這有利於調試消息。以下是期望結果的表示。
清單 1. printTimestampMessage
void printTimestampMessage(String message) {
System.out.print(message);
System.out.print("; Timestamp= ");
System.out.println(System.currentTimeMillis());
}
第二個示例接受 3 個基於日期的參數,並返回一個數字值,表示該日期對應的是星期幾。
清單 2. getDayOfWeek
int getDayOfWeek(int year, int month, int date) {
int result;
Calendar calendar = Calendar.getInstance();
calendar.set(year, month, date);
result = calendar.get(Calendar.DAY_OF_WEEK);
return result;
}
第一步是填入在上一節最後一步中創建的新 EPackageX 元素下的 3 個必需屬性。如果在建模窗口下看不到 Properties 選項卡,可以從上下文菜單中選擇 Show Properties View。在這個示例中,我們的包名為 mypackage。
圖 5. EPackageX 屬性
接下來,向 mypackage 添加一個新 EClassX。可以在 mypackage 突出顯示時使用上下文菜單完成此任務。填入 name 屬性,為類提供一個名稱(比如 MyClass),向新類添加兩個 EOperationImpl 元素,並為它們指定方法名 printTimeStampMessage 和 getDayofWeek。然後,向每一個操作添加 Ecore 參數。
圖 6. EOperationImpl getDayOfWeek()
圖 7. getDayOfWeek() 屬性
上面的操作 printTimestampMessage() 接受一個 EString 類型的參數,而 getDayOfWeek() 接受 3 個 EInt 類型的參數。此外,操作 getDayOfWeek 返回一個 EInt,這可以在 property 屬性 EType 下進行配置(參見圖 7)。
剖析 EOperationImpl
到現在為止,我們僅使用了繼承的 Ecore 元素和屬性。現在是時候使用我們擴展的元模型元素來構建 Java 實現了。
LocalVariable
查看一下圖 8,printTimestampMessage() 將需要兩個 LocalVariable 元素 — 一個為 EString 類型,另一個為 ELong 類型。
圖 8. printTimestampMessage()
圖 9. LiteralAssignment
在圖 9 中,Value 屬性的字符串被內聯到 LiteralAssignment。您可以設想一個不同的元模型,其中的文字值(常量)被建模為單獨的元素。
接下來,我們插入一個 LiteralAssignment 類型的元素,它允許選擇一個 LocalVariable 並為其分配值。在本例中,我們選擇 String 變量並提供上面的原型方法中的文本值(記住在文本兩邊加上引號)。
DataType
再次查看上圖,注意,有一個名為 SystemType 的 Ecore DataType,它是 java.lang.System 的一個包裝器。必須將其添加到我們的 mypackage 包,因為它將會被隨後的 Invoke 元素引用。
Statement
添加到這個操作的第一個 Statement 是 SystemType 中的靜態方法 currentTimeMillis() 的一個 Invoke,已經在上面定義了。
圖 10. 調用 currentTimeMillis() 屬性
根據我們的元模型(我們將在下一節提供代碼模板),上面的 Invoke 將轉換為 Java 語句:timestamp = java.lang.System.currentTimeMillis();。
下一個 Invoke 與之前的那個稍有不同。首先,沒有 Assignment。其次,我們將把 message 參數的引用作為 Args 屬性的一個參數。
圖 11. 調用 out.print 屬性
操作中的第 3 個(最後一個)Invoke 是一個使用 LocalVariabletimestamp 作為單個參數的 println()。這就完成了具體操作 printTimestampMessage() 的建模。
讓我們看看第二個 EOperationImplgetDayOfWeek() 的完整模型。
圖 12. getDayOfWeek()
DataTypes
在模型的底部,我們創建了一個額外的 DataType,名為 CalendarType,這是該操作所必需的。
LocalVariables
在操作模型的 3 個 LocalVariable 中,我們主要關注稱為 result 的 LocalVariable,因為它將會保存執行完操作的最後一條語句之後返回的值。在 EOperationImpl 屬性中有一個名為 Return Ref 的屬性,而且在我們的實現中,我們使用下拉菜單選擇 LocalVariable 結果。
Statement
正如圖 12 所示,3 個 LocalVariable 之後是 3 個 Statement。第一個是 Invoke,它使用 CalendarType 元素上的 getInstance(),為 calendar 變量分配一個值,與圖 10 中的操作類似。
接下來是對 calendar 變量執行的 set() 方法的 Invoke,現在它傳遞 3 個與 EOperationImpl 參數(year、month 和 date)相對應的 Arg。
圖 13. 帶有參數的 set()
圖 14. FieldReferenceAssignment
根據我們的元模型,這個元素將會生成與 DAY = Calendar.DAY_OF_WEEK; 類似的 Java 代碼。
在圖 15 中,DAY 變量用於這個 EOperationImpl 的最後一個 Invoke:一個 get(),其返回值被分配給變量 result(我們的實現的 Return Ref)
圖 15. Return Ref
實現動態模板
我們現在設計了一個擴展的元模型,並用其描述了一個具體的模型 My.ecore(請參見上述的 EMF V2.5+ 文件名稱說明)。現在終於可以用 JET 最終實現一些代碼實現了。要查看 JET 模板的語法突出顯示功能,您需要安裝 JET Editor Plugin。
默認情況下,在為模型生成代碼時,EMF 不會使用動態模板。它使用預構建的 Java 類。要開始定制 JET 模板,我們需要從插件 JAR 文件 org.eclipse.emf.codegen.ecore_2.3.0.XYZ.jar 復制一些文件,其中 XYZ 是 Eclipse 插件文件夾中您的 EMF 版本的時間戳。本文使用 org.eclipse.emf.codegen.ecore_2.3.0.v200706262000.jar。要復制這些文件,請使用任意一種解壓縮工具打開 JAR 文件,並執行以下操作:
從這個 JAR 文件將模板目錄提取到您的具體模型的 Java 項目中。
在模板/模型中創建一個目錄,名為 Class。
在 Class 文件夾中創建一個新的空文件,名為 implementedGenOperation.TODO.override.javajetinc 。
由名稱可以看出,第 3 步中的新文件是一個 JET 模板,我們將在其中加入模型對象 EOperationImpl 的代碼生成邏輯。默認情況下,這個文件並不存在,因為 EMF 只為每個 EOperation 提供一個空的方法簽名。一旦激活了動態模板功能,我們的新文件將被作為 Java 方法體自動包括,正如 EOperationImpl 所定義的。
以下是 implementedGenOperation.TODO.override.javajetinc 的完整代碼。
清單 3. implementedGenOperation
// created by implementedGenOperation.TODO.override.javajetinc ><% >if ( ! (genOperation.getEcoreOperation() instanceof EOperationImpl) ) { %> > // TODO: implement this method > // Ensure that you remove @generated or mark it @generated NOT > throw > new UnsupportedOperationException(); ><% } else { %> > // ** EOperationX implementation ** ><% EOperationImpl opx = (EOperationImpl)genOperation.getEcoreOperation(); >Statement stm = null; >Iterator iterator = null; >EList<LocalVariable> pList = opx.getLocalVariables(); >LocalVariable lvar = null; >String iname = null; >StringBuffer paramsString = null; >StringBuffer varString = null; >for (int i = 0;i < pList.size(); i++) { > lvar = pList.get(i); > iname = lvar.getEType().getInstanceClassName();%> > <%=iname%> > <%=lvar.getName()%><% > if (iname.startsWith("java")) { %> = null > <% } %>; ><% } >iterator = opx.getStatements().iterator(); >while (iterator.hasNext()) { > paramsString = new StringBuffer(); > varString = new StringBuffer(); > iname = null; > stm = (Statement)iterator.next(); > if (stm instanceof LiteralAssignment) {%> > <%= stm.getAssignment().getName()%> = <%=\ >((LiteralAssignment)stm).getValue()%>; > <%} else > // > if (stm instanceof FieldReferenceAssignment) { > Access ax = (Access)stm; > if (stm.getAssignment() != null) { > varString.append(stm.getAssignment().getName()); > varString.append(" = "); > } > if ( ax.getStaticType() != null) { > // STATIC > iname = ax.getStaticType().getInstanceClassName(); > } else { > // NON STATIC > iname = ax.getTarget().getName(); > } %> > <%=varString.toString()%><%=iname%>.<%=ax.getAccessName()%>; > <% } else > if (stm instanceof Invoke) { > // INVOKE > Invoke iv = ((Invoke)stm); > if (stm.getAssignment() != null) { > varString.append(stm.getAssignment().getName()); > varString.append(" = "); > } > for (int p = 0; p < iv.getArgs().size(); p++) {\ >paramsString.append(iv.getArgs().get(p).getName()); > if ( p + 1 < iv.getArgs().size() ) { > paramsString.append(" , "); > } > } > if (iv.getStaticType() != null) { > // STATIC > iname = iv.getStaticType().getInstanceClassName(); > } else { > // NON STATIC > iname = iv.getTarget().getName(); > } %> > <%=varString.toString()%><%=iname%>.<%=iv.getAccessName()\ >%>(<%=paramsString.toString()%>); > <% } >} // STATEMENTS > if (opx.getReturnRef() != null) { %> > return > <%=opx.getReturnRef().getName()%>; > <% } >} // EOPERATIONIMPL %>
對 JET 的詳細討論超出了本文的范圍。但是,因為 JET 模板對我們的操作過程至關重要,我們將在偽代碼方面回顧一下模板的內容。請記住,在處理模板之前,第一個變量 genOperation 已經被 Ecore/JET 預初始化。
清單 4. genOperation 被 Ecore/JET 預初始化
Is this GenOperation is an EOperationImpl?
If false, emit default UnsupportedOperationException
STOP;
Else, cast it to EOperationImpl;
continue;
Find and declare all elements of type LocalVariable, initializing Java Objects to null;
Iterate through all Statements;
Emit Java code according to the subtype;
Does the implementation return something?
If yes, emit the return statement;
在構建具體模型之前,需要執行一些操作。首先,在 templates/model/Class.javajet 頂部,我們必須將以下內容添加到導入列表(標記為粗體的前兩行):
<%@
jet
package="org.eclipse.emf.codegen.ecore.templates.model"
imports="ecorex.* org.eclipse.emf.common.util.* \
java.util.* org.eclipse.emf.codegen.ecore.genmodel.*"…
當然,EcoreX 包是經過擴展的元模型。接下來,我們需要為我們的具體模型(My.ecore,類型為 '.ecorex')創建和配置一個 EMF(GenModel)。為此,在模型上右鍵單擊並選擇 New > Other > Eclipse Modeling Framework > EMF Model(對於 EMF V2.5+ [Galileo, Helios],應選擇 New > Other > Eclipse Modeling Framework > EMF Generator Model。)創建完成之後,需要在屬性組 Templates & Merge 下配置 3 個屬性,在 Model 下配置第四個屬性。
圖 16. GenModel — Templates & Merge
將 Dynamic Templates 設置為 true。
指定 Template Directory。
將 EMFX(擴展的元模型插件 ID)添加到 Template Plug-in Variables。
最近版本:在 Model 組屬性下,將 Suppress Interfaces 設置為 true。
現在可以進行構建了,右鍵單擊 GenModel 並選擇 Generate Model Code。如果一切順利,在具體的 Test 項目(我們的項目稱為 Test2)的源文件夾(src)中,您應該可以看到生成的 Java 源代碼包和類,其中一個名為 mypackage.impl.MyClassImpl.java。打開該文件,您應該會看到兩個生成的方法。
清單 5. MyClassImpl.java
public
void printTimestampMessage(String message) {
// created by implementedGenOperation.TODO.override.javajetinc
// ** EOperationX implementation **
java.lang.String timestampStr = null;
long timestamp;
timestampStr = "; Timestamp = ";
timestamp = java.lang.System.currentTimeMillis();
java.lang.System.out.print(message);
java.lang.System.out.print(timestampStr);
java.lang.System.out.println(timestamp);
}
public
int getDayOfWeek(int year, int month, int date) {
// created by implementedGenOperation.TODO.override.javajetinc
// ** EOperationX implementation **
int result;
int DAY;
java.util.Calendar calendar = null;
calendar = java.util.Calendar.getInstance();
calendar.set(year , month , date);
DAY = java.util.Calendar.DAY_OF_WEEK;
result = calendar.get(DAY);
return result;
}
可以添加一個 main 方法測試這個類。
警告和故障診斷
Ecore 文件命名 (EMF V2.5+)
在 EMF V2.5 之前,正如上面的幾個屏幕快照所示,從一個擴展了的 Ecore 模型生成的具體模型應該保留 '.ecorex' 的擴展名(如創建時的向導所建議的那樣)。這有助於區別擴展了的模型與 ‘初級的’ Ecore 模型。然而,在 EMF 的最新版本中,genmodel 向導(如在圖 16 之前所解釋的)不接受除 .ecore 之外的其他文件擴展名。
JET Editor 局限性
要獲得 JET 模板的語法突出顯示功能,您需要安裝 Eclipse JET Editor(JET Editor Plugin 最近已經從 EMF 遷移到 M2T)。
但是,在撰寫本文時,JET Editor 的最新版本不能正確處理 Java 內容幫助或嵌套 JET 包含文件(比如 .javajetinc 文件)的動態編譯。此外,為了確保構建成功,只能在父文件(比如上面的 Class.javajet)中指定導入操作,而不能在包含的文件中指定。
實際上,使用一些額外配置(即,使用項目的上下文菜單),您可以將 EMF 動態模板項目(本文示例中的 Test2)轉換為 JET 項目。在實踐中,上面提及的局限性以及 EMF 和 M2T/JET 之間缺少集成,使得這種方法不太可行。
因此,很難捕獲和改正包含的模板文件中的錯誤。由於在生成最終代碼之前,JET 模板首先會被編譯為一種中間 Java 文件(默認情況下位於一個隱藏的 Java 項目 JETEmitter 中),所以通過從 Eclipse 的 Package Explorer 視圖中刪除過濾器,您能夠看到這些編譯錯誤。如果這只是模板文件中的格式錯誤,在構建期間將會出現一個 Eclipse 彈出窗口。或許在未來的 JET 版本中,我們會看到更多的改進功能。
未進行模型驗證
本文中的示例未使用 EMF Validation Framework 或 OCL 功能。所以,模型中的不一致性將會導致構建失敗,例如,一個 EOperationImpl 可以聲明某種返回類型,但是 Return Ref 屬性可以引用不同的類型或者為空。在構建模型期間,將不會發現這些錯誤,而且生成的代碼將無法編譯。可以對元模型進行改進,使用 OCL 增強完整性和約束。
結束語
我們看到了如何擴展 Ecore 元模型,將合成的 Java 方法中的編程式行為概念化。通過導入 Ecore 本身,我們擴展了一些 Ecore 模型元素 — 尤其是 EOperation。然後構建了元模型,並使用編輯器設計了一個具體的測試模型,包括以 EOperationImpl 形式建模的兩個 Java 方法。我們配置並構建了 JET 模板,用於為 EOperationImpl 生成代碼。