摘要:使用元數(shù)據(jù)包中包含了中每一個(gè)被建模類對(duì)應(yīng)的接口。任何對(duì)象的元數(shù)據(jù)是使用的實(shí)現(xiàn)來(lái)表示的。加載模型的序列化形式是個(gè)在運(yùn)行期間獲取元數(shù)據(jù)的有效方法。反射提供一個(gè)反射式,可以檢查對(duì)象的元數(shù)據(jù)以及一般地訪問(wèn)和操縱數(shù)據(jù)。
使用元數(shù)據(jù)
Java包org.eclipse.emf.ecore中包含了Ecore中每一個(gè)被建模類對(duì)應(yīng)的接口。任何EMF對(duì)象的元數(shù)據(jù)是使用Ecore的實(shí)現(xiàn)(implementation)來(lái)表示的。這些信息在運(yùn)行期間(runtime)總是可獲取的,所以我們可以使用Ecore API來(lái)查詢模型的結(jié)構(gòu),還可以使用反射式EObject API來(lái)訪問(wèn)和操縱實(shí)例中的數(shù)據(jù)。此外,僅通過(guò)實(shí)例化 Ecore,我們就可以在運(yùn)行期間動(dòng)態(tài)定義一個(gè)模型。
本章我們仔細(xì)研究EMF對(duì)象暴露的元數(shù)據(jù),并且看看元數(shù)據(jù)是如何被反射和動(dòng)態(tài)EMF機(jī)制所利用的。
在第10章中,EMF生成了一個(gè)包接口,它為定義在包中的類型提供了對(duì)元數(shù)據(jù)的簡(jiǎn)單訪問(wèn)。
這個(gè)生成的接口僅僅是個(gè)便利:它提供的所有訪問(wèn)也可以在基本接口中使用泛型方法(generic methods)。例如,接口EPO2Package,它從ExtendedPO2模型中生成,它提供了有個(gè)便捷的方法來(lái)訪問(wèn)PurchaseOrder類。我們可以像這樣使用它:
EClass poClass = epo2Package.getPurchaseOrder();
不使用這個(gè)生成類,我們可以使用EPackage接口中的getEClassifier方法來(lái)簡(jiǎn)單檢索類,如下:
EClass poClass = (EClass)epo2Package.getEClassifier("PurchaseOrder");
我們可以類似地訪問(wèn)生成的包提供的屬性,引用以及其他所有內(nèi)容。
加載模型的序列化形式是個(gè)在運(yùn)行期間獲取元數(shù)據(jù)的有效方法。事實(shí)上,這也是在內(nèi)存中創(chuàng)建模型的最初方法。但是,從磁盤(pán)中讀取,解析XML,構(gòu)建整個(gè)模型是非常昂貴的操作,所以對(duì)于給定的模型,我們應(yīng)該只做一次。
我們希望可以檢查兩個(gè)EMF對(duì)象是否是同一個(gè)類的實(shí)例。
包注冊(cè)表( package registry)提供對(duì)含有元數(shù)據(jù)的包的訪問(wèn),不管這些包是如何被初始化的。
使用java.util.Map填充包注冊(cè)表。鍵(key)是String類型命名空間URI,EPackage是值(value)。一些值也可能是EPackage的實(shí)例。
使用注冊(cè)表鍵入的getEPackage()方法來(lái)注冊(cè)生成的包。
當(dāng)EMF獨(dú)立運(yùn)行時(shí),生成的包通常是在構(gòu)建的時(shí)候注冊(cè)的,如下:
EPO2Package epo2Package = EPO2Package.eINSTANCE;
注冊(cè)包還有其他兩種通常的機(jī)制:URIConverters和xsi:schemaLocation,它們與沒(méi)有生成代碼的模型一起使用。包從它們的序列化形式中加載并自動(dòng)注冊(cè)。
一旦包被注冊(cè),就可以在任何需要它的時(shí)候輕松訪問(wèn):在合適的 EPackage.Registry上調(diào)用getEPackage()方法。例如,基于名稱和包的命名空間URI,使用全局包注冊(cè)表來(lái)獲取特定EClassifier,如下:
public static EClassifier getEClassifier(String nsURI, String name) { EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI); return ePackage == null ? null : ePackage.getEClassifier(name); }
我們也可以使用這個(gè)方法來(lái)從ExtendedPO2模型中獲取特定的類,如下:
EClassifier poClass = getEClassifier( "http://www.example.com/epo2.ecore","PurchaseOrder"));
回顧EPackage.Registry接口,它包含一個(gè)getEFactory() 方法,這個(gè)方法可以獲取已注冊(cè)的包的工廠(factory)?;仡櫟谖逭?,每個(gè)EPackage總有一個(gè)關(guān)聯(lián)的EFactory,這個(gè)EFactory可以被用來(lái)初始化定義在包中的分類器(classifiers)。getEFactory() 方法可以作為獲取包以及調(diào)用包的getEFactoryInstance() 的一種捷徑。
EMF提供一個(gè)反射式API,可以檢查對(duì)象的元數(shù)據(jù)以及一般地訪問(wèn)和操縱數(shù)據(jù)。EMF反射類似于Java反射,但是EMF反射運(yùn)行于稍微高一點(diǎn)的層次。
創(chuàng)建對(duì)象EMF對(duì)象是使用工廠來(lái)創(chuàng)建的。例子如下:
PurchaseOrder order = epo2Factory.createPurchaseOrder();
生成的工廠擴(kuò)展了基礎(chǔ)的工廠接口,EFactory繼承了可以反射式創(chuàng)建對(duì)象的通用的create() 方法。這種方法使用一個(gè)EClass來(lái)指定哪個(gè)類應(yīng)該被實(shí)例化:
EClass poClass = ... EFactory epo2factory = poClass.getEPackage().getEFactoryInstance(); EObject order = epo2factory.create(poClass);
可以看出創(chuàng)建一個(gè)對(duì)象,所需要的僅是一個(gè)EClass。從其中我們可以輕松地獲取它包含的包,以及對(duì)應(yīng)的工廠。事實(shí)上,有個(gè)捷徑,我們可以使用一個(gè)EMF多功能的方法來(lái)實(shí)現(xiàn):
EObject order = EcoreUtil.create(poClass);
我們?nèi)绾巫屢粋€(gè)EClass用于創(chuàng)建對(duì)象?根本上,它來(lái)自于包。回顧上一章從注冊(cè)表中訪問(wèn)包。首先,基于命名空間URI來(lái)檢索包,然后從包中獲取命名的類,最后,反射地實(shí)例化這個(gè)類:
public static EObject createEObject(String nsURI, String name) { EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI); EClass eClass = (EClass)ePackage.getEClassifier(name); return ePackage.getEFactoryInstance().create(eClass); }
我們現(xiàn)在可以創(chuàng)建一個(gè)PurchaseOrder類的實(shí)例:
EObject order = createEObject("http://www.example.com/epo2.ecore","PurchaseOrder");
這實(shí)際上與EMF的XML資源實(shí)現(xiàn)(XML resource implementation)在加載過(guò)程中必須在基于命名空間限定類型名稱來(lái)創(chuàng)建對(duì)象時(shí)非常相似。
反射的對(duì)象創(chuàng)建總是留給我們一個(gè)EObject的實(shí)例,EMF的用于建模對(duì)象的高性能基礎(chǔ)接口。接下來(lái),我們就可以使用這個(gè)接口來(lái)檢索對(duì)象的元數(shù)據(jù)以及對(duì)其特性(features)的一般性訪問(wèn)。
既然我們現(xiàn)在知道了如何去檢查元數(shù)據(jù)以及創(chuàng)建對(duì)象,接下來(lái)就只需要一種方式來(lái)反射地訪問(wèn)屬性(attribute)和引用(reference)。這就用到了反射的 EObject API。
我們通常訪問(wèn)元數(shù)據(jù)使用的是EObject的eClass()方法,其返回的是描述對(duì)象的EClass。然后我們可以使用元數(shù)據(jù),通過(guò)反射性eGet(), eSet(), eIsSet(), eUnset() 方法來(lái)訪問(wèn)和設(shè)置對(duì)象屬性和引用的值。這些方法每個(gè)都有EStructuralFeature來(lái)識(shí)別將被訪問(wèn)的特性(feature)。
下面是打印出任意一個(gè)對(duì)象的名稱以及所有屬性的值的簡(jiǎn)單方法:
public static void printAttributeValues(EObject object) { EClass eClass = object.eClass(); System.out.println(eClass.getName()); for (Iterator iter = eClass.getEAllAttributes().iterator(); iter.hasNext(); ) { EAttribute attribute = (EAttribute)iter.next(); Object value = object.eGet(attribute); System.out.print(" " + attribute.getName() + ": " + value); if (object.eIsSet(attribute)) { System.out.println(); } else { System.out.println(" (default)"); } } }
getEAllAttributes()方法返回所有的屬性。對(duì)于每個(gè)屬性,調(diào)用反射性eGet()方法來(lái)檢索屬性的值。我們也調(diào)用eIsSet()來(lái)決定是否將額外的信息“(default)”附加到打印的值后面。對(duì)于Item的實(shí)例,輸出如下:
Item productName: Tire quantity: 4 USPrice: 50 comment: null (default) shipDate: null (default) partNum: 622-RT
接下來(lái)看一個(gè)修改數(shù)據(jù)的例子,尋找一個(gè)名稱為"USPrice"的整型特性,把其現(xiàn)有的值減少10%,如下:
public static void adjustPrice(EObject object) { EStructuralFeature feature =object.eClass().getEStructuralFeature("USPrice"); if (feature != null &&feature.getEType() == EcorePackage.Literals.EINT) { int price = ((Integer)object.eGet(feature)).intValue(); object.eSet(feature, new Integer(price * 9 / 10)); } }
Ecore的EInt數(shù)據(jù)類型代表了原始(primitive) Java類型int,所以我們?nèi)匀槐仨毷褂脦в?strong>eGet()和eSet()整型封裝類。
除了值的類型總是EObject之外,在對(duì)象中操縱引用(reference)與檢索屬性值類似。EObject也有其他方法可以幫忙,例如,如果想要尋找對(duì)象的容器(container),你就傾向于編寫(xiě)方法findContainer(),它可以遍歷所有的對(duì)象引用,并檢查isContainer(),最后返回第一個(gè)非null的值:
public static EObject findContainer(EObject object) { for (Iterator i = object.eClass().getEAllReferences().iterator();i.hasNext(); ) { EReference reference = (EReference)i.next(); if (reference.isContainer()) { EObject value = (EObject)object.eGet(reference); if (value != null) { return value; } } } return null; }
EObject總是知道自己的容器,并且通過(guò)eContainer()方法使其可用。如果這個(gè)方法返回的是null,那么這個(gè)對(duì)象就沒(méi)有被包含。
類似地,你可能想獲取某個(gè)對(duì)象包含的所有對(duì)象。當(dāng)然也可以使用上面類似地方法,然而,有個(gè)更便捷的方式來(lái)實(shí)現(xiàn):使用EObject API eContents()。它可以返回一個(gè)所有對(duì)象包含的引用的只讀列表(list)視圖,此外,eCrossReferences() 返回的是非包含的引用的視圖。
我們可以使用eContents()來(lái)建立價(jià)格調(diào)整的例子:
public static void adjustPrices(EObject object) { adjustPrice(object); for (Iterator i = object.eContents().iterator(); i.hasNext(); ) { adjustPrices((EObject)i.next()); } }
這個(gè)例子使用了遞歸訪問(wèn)的方法,如果使用eAllContents()就可以消除遞歸:
public static void adjustPrices(EObject object) { adjustPrice(object); for (Iterator i = object.eAllContents(); i.hasNext(); ) { adjustPrice((EObject)i.next()); // No recursion! } }
(可以再補(bǔ)充抽象類相關(guān))
動(dòng)態(tài)EMF(Dynamic EMF) 生成的代碼也可以看作是類型安全(type-safe) API,而反射性EObject API的動(dòng)態(tài)實(shí)現(xiàn)( dynamic implementation),可以看做是類型指定(type-specific) API。相比于動(dòng)態(tài)實(shí)現(xiàn),生成的代碼要更高效且使用更少的內(nèi)存,以及提供更快的數(shù)據(jù)訪問(wèn)。然而,如果你不需要提供一個(gè)類型安全API或者修改默認(rèn)的行為(behavior),動(dòng)態(tài)實(shí)現(xiàn)則是更好的選擇,它允許模型能被更自由地共享,以及在不維護(hù)生成的類的情況下進(jìn)行演化。這就是動(dòng)態(tài)和靜態(tài)實(shí)現(xiàn)之間的正常權(quán)衡(trade-off)。
同時(shí)兼得代碼生成的高性能和動(dòng)態(tài)EMF的靈活性的一種可能的方法是,在運(yùn)行期間調(diào)用代碼生成器,然后動(dòng)態(tài)地加載生成的類。
?。↗DT JIT 以及其他未完待續(xù))
?。ㄅc第8章和第9章相關(guān))
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67473.html
摘要:定義模型元模型用于表示中模型的模型稱為。用于表示的類型,它可以是基本類型,例如或?qū)ο箢愋偷?。此外,因?yàn)槭秦浳锏娜萜鞑?huì)在其中將貨物作為孩子序列化,所以需要標(biāo)識(shí)出。 EMF介紹 為了理解EMF究竟是什么,你只需要知道一件事:模型(model)是什么?模型的目的是什么? EMF不要求全新的方法論亦或是任何復(fù)雜的建模工具。只需要從Eclipse的Java開(kāi)發(fā)工具著手開(kāi)始。 EMF將建模概念...
摘要:定義模型元模型用于表示中模型的模型稱為。用于表示的類型,它可以是基本類型,例如或?qū)ο箢愋偷取4送?,因?yàn)槭秦浳锏娜萜鞑?huì)在其中將貨物作為孩子序列化,所以需要標(biāo)識(shí)出。 EMF介紹 為了理解EMF究竟是什么,你只需要知道一件事:模型(model)是什么?模型的目的是什么? EMF不要求全新的方法論亦或是任何復(fù)雜的建模工具。只需要從Eclipse的Java開(kāi)發(fā)工具著手開(kāi)始。 EMF將建模概念...
摘要:生成的包首次被訪問(wèn)時(shí),在全局包注冊(cè)表中自動(dòng)地注冊(cè)。然而,類似于資源工廠注冊(cè)表,這種顯式注冊(cè)的過(guò)程僅當(dāng)獨(dú)立運(yùn)行時(shí)被要求,在下運(yùn)行時(shí)通過(guò)擴(kuò)展指針來(lái)自動(dòng)地完成。通過(guò)使用合適的資源工廠,就可以確定被產(chǎn)生的和被使用的持久化形式。 持久化(Persistence) EMF擁有一個(gè)強(qiáng)大的模型持久化框架。通過(guò)一個(gè)高度可定制資源實(shí)現(xiàn)(resource implementation)來(lái)支持XML序列化...
摘要:生成的包首次被訪問(wèn)時(shí),在全局包注冊(cè)表中自動(dòng)地注冊(cè)。然而,類似于資源工廠注冊(cè)表,這種顯式注冊(cè)的過(guò)程僅當(dāng)獨(dú)立運(yùn)行時(shí)被要求,在下運(yùn)行時(shí)通過(guò)擴(kuò)展指針來(lái)自動(dòng)地完成。通過(guò)使用合適的資源工廠,就可以確定被產(chǎn)生的和被使用的持久化形式。 持久化(Persistence) EMF擁有一個(gè)強(qiáng)大的模型持久化框架。通過(guò)一個(gè)高度可定制資源實(shí)現(xiàn)(resource implementation)來(lái)支持XML序列化...
閱讀 3474·2021-11-25 09:43
閱讀 1081·2021-11-15 11:36
閱讀 3325·2021-11-11 16:54
閱讀 3990·2021-09-27 13:35
閱讀 4381·2021-09-10 11:23
閱讀 5788·2021-09-07 10:22
閱讀 3048·2021-09-04 16:40
閱讀 779·2021-08-03 14:03