除了普通的類(接口)以外,在類圖裡可以定義一些特殊的元素,比較常見的是枚舉類型 、自定義類型,它們對於一個完整可用的模型也是必不可少的,這篇帖子主要介紹EMF裡它們 的使用方法。另外,由於EMF對Map的支持比較特別,所以在這裡也簡要介紹一下Map類型的定 義方法。
枚舉類型
繼續前面帖子的例子,現在要為產品增加一個評分屬性,評分值可以是好中差之一,像這 樣屬性值只能是有限幾個值之一的屬性就應該定義為枚舉類型(Enumeration)。在類圖裡首 先創建一個名為Score的枚舉類型,然後為它增加三個可選值,每個值對應一個唯一的整數值 作為標識;然後給 Product類型添加一個名為score的屬性,這時的類型列表裡已經比原來多 了Score類型,我們就選擇它作為score屬性的類型。重新生成一遍代碼,你會發現增加了 Score類(不是接口),運行新生成的編輯器會看到,產品對象的屬性裡增加了評級,見圖1 。
圖1 枚舉類型的屬性以下拉列表方式編輯
自定義類型
EMF雖然對大多數java類型做了包裝,但是有些情況需要我們使用沒有被包含的類型,例 如在設計圖形化的編輯器(例如類圖編輯器)時,圖形節點一般允許選擇背景顏色,這就需 要一個org.eclipse.swt.graphics.RGB類型的成員變量,而RGB類是SWT提供的類,所以不能 通過創建一個同名類的方式實現,這時就要使用自定義類型。類似的道理,在必須利用遺產 項目(Legacy)代碼的時候,自定義類型也是必須的。
現在為Product節點增加這樣一個名為background的成員變量,步驟如下:首先在類圖上 新建一個名為RGB的自定義類型(data- type,見圖2),將它的Instance Class屬性設置為 org.eclipse.swt.graphics.RGB;然後給Product類添加一個成員變量background,類型選擇 為剛建立的RGB;現在重新生成一遍代碼,可以看到Product.java裡已經多了這個成員變量, 其類型為 org.eclipse.swt.graphics.RGB(因為org.eclipse.swt.graphics.RGB是屬於 org.eclipse.swt這個插件的,所以要為com.my.shop項目增加對org.eclipse.swt的依賴才能 正確編譯)。
圖2 新建自定義類型
不過到這裡還沒有完成全部工作。以為EMF對這個RGB類一無所知 ,所以它不可能為我們實現RGB對象的持久化,即將RGB類型的數據保存到(缺省格式的)xml 文件中。要解決這個問題得在生成的ShopFactoryImpl裡做一些定制,具體來說是修改 createRGBFromString ()和convertRGBToString()這兩個方法,很明顯它們的作用分別是從 字符串創建RGB對象以及將RGB對象轉換成字符串,我們把它們改為下面這樣。
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public RGB createRGBFromString (EDataType eDataType, String initialValue) {
String[] values = initialValue.split(",");
int red=Integer.parseInt(values [0]);
int green=Integer.parseInt(values[1]);
int blue=Integer.parseInt(values[2]);
RGB rgb = new RGB(red,green,blue);
return rgb;
}
/**
* <!-- begin-user-doc -- >
* <!-- end-user-doc -->
* @generated NOT
*/
public String convertRGBToString(EDataType eDataType, Object instanceValue) {
RGB rgb = (RGB) instanceValue;
return rgb.red + "," + rgb.green + "," + rgb.blue;
}
這些代碼可以把RGB對象轉 換為形如“255,90,150”的格式,EMF在持久化時遇到RGB類型的對象就按這樣的 格式寫到文件裡。
使用EMap
在類圖裡可以指定各種類型的成員變量,但使用Map類型則有點特別,直接定義一個EMap 類型的成員變量是不可以的,因為EMap並不是繼承自 java.util.Map而是EList,也就是說 EMF使用EList來模擬實現Map的功能(為什麼要這樣實現我還沒弄清楚)。要實現一個EMap 類型的成員變量,在類圖裡要參考下面的方式進行定義。
現在我們要為產品類增加一個EMap類型的屬性,用這個屬性來記錄產品每月的銷售數量, 它維護一個日期字符串(如"2005-09")到當月銷售數量的映射。首先,定義一個名為 StringToIntegerMapEntry的新類,這個類將作為Map裡的項(Entry);為這個類增加兩個屬 性:字符串類型的key和整數類型(Integer或int均可)的value;在屬性視圖裡把這個類的 Instance Class Name屬性設置為java.util.Map$Entry;這樣MapEntry類就定義好了,在需 要EMap類型變量的類裡引用它就可以了,注意對它的引用必須是包含關系(即“組合”而非 “聚合”,類圖上連接線用黑色菱形修飾),並且是一對多的。
重新生成一遍代碼,在Product接口裡salesMap成員變量是這樣定義的(因為EMF會檢測到 我們正在定義一個Map):
/**
* Returns the value of the '<em><b>Sales Map</b></em>' map.
* The key is of type {@link java.lang.String},
* and the value is of type {@link java.lang.Integer},
* <!-- begin-user-doc -->
* <p>
* If the meaning of the '<em>Sales Map</em>' containment reference isn't clear,
* there really should be more of a description here
* </p>
* <!-- end-user-doc -->
* @return the value of the '<em>Sales Map</em>' map.
* @see com.my.shop.ShopPackage#getProduct_SalesMap()
* @model mapType="com.my.shop.StringToIntegerMapEntry" keyType="java.lang.String" valueType="java.lang.Integer"
* @generated
*/
EMap getSalesMap();
這樣,在生成的編輯器裡可以對每個產品增加新的項,每項包含key和value兩個值;在程 序裡,則應該使用 Product#getSalesMap().put()方法向這個Map裡增加數據,因為我們定義 的Map項是字符串到整數類型的,所以put()的時候value參數必須是整數對象 (java.lang.Integer),否則會拋出ClassCastException。請記住,不要直接指定 EMap類 型,包含MapEntry即可,如果key或value不是普通類型,則使用名為key或value的引用。
圖3 編輯器裡的EMap類型屬性
經過上面幾處改動,現在我們的商店模型如下圖所示,點此下載項目打包。
圖4 修改後的模型