中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Eclipse Modeling Framework(EMF)概要

以下文章出自www.eclipse.org。本人僅作中文翻譯,未作任何其它修改。

EMF概述

文檔原出處:http://eclipse.org/emf/docs.php?doc=references/overview/EMF.html

最后修改時(shí)間: 2005616

 

本文為EMF及其代碼生成模式提供了一個(gè)基本的概述。要了解EMF所有功能的詳細(xì)描述,請(qǐng)參見《Eclipse Modeling Framework》(Addison Wesley, 2003),或者框架自身的Javadoc文檔。

概論

EMF是一個(gè)Java框架與代碼生成機(jī)制,用來構(gòu)建基于結(jié)構(gòu)化模型的工具或其它應(yīng)用系統(tǒng)。它們可以帶給你面向?qū)ο蠼5乃枷耄?/span>EMF幫助你快速地將你的模型轉(zhuǎn)變?yōu)楦咝У?、正確的、以及易用的定制Java代碼,只需要一個(gè)很低的入門成本,EMF就可以為你提供這些益處。

那么,當(dāng)我說“模型”時(shí)到底意味著什么?當(dāng)談?wù)撃P蜁r(shí),我們一般都會(huì)想到類圖、協(xié)作圖、狀態(tài)類,等等。UML為這些圖定義了標(biāo)準(zhǔn)的符號(hào)。聯(lián)合使用各種UML圖,可以詳細(xì)說明一個(gè)應(yīng)用系統(tǒng)的完整模型。這個(gè)模型可能純粹只用作文檔,或者通過適當(dāng)?shù)墓ぞ撸梢员挥脕碜鳛檩斎雰?nèi)容生成一部分或全部應(yīng)用系統(tǒng)代碼。

要能夠做到這些的建模工作一般都需要昂貴的面向?qū)ο蟮姆治雠c設(shè)計(jì)工具(OOA/D),你可能對(duì)我們的結(jié)論有疑問,但是,EMF提供了一個(gè)低成本的入口。我們說這個(gè)的理由是一個(gè)EMF模型只需要你擁有在UML中建模所需知識(shí)的一小部分即可,主要是簡單的類的定義,以及它們的屬性與關(guān)系,針對(duì)這些,不需要使用一個(gè)全尺寸的圖形化建模工具。

EMF使用XMI作為模型定義的規(guī)范形式,你有多種方法可以得到這種格式的模型:

·            直接使用XML或文本編輯器來創(chuàng)建XMI文檔。

·            從建模工具,如Rose,中導(dǎo)出XMI文檔。

·            帶有模型屬性的注解Java接口。

·            描述模型序列化格式的XML Schema

第一種方法最直接,但一般只對(duì)XML高手有吸引力。若你已經(jīng)使用了全尺寸的建模工具,則第二種方法是最可取的。第三種方法只需要有一個(gè)基本的Java開發(fā)環(huán)境就可以低成本地?fù)碛?/span>EMF帶來的好處、以及它的代碼生成能力。在創(chuàng)建一個(gè)需要讀寫特定的XML文件格式的應(yīng)用系統(tǒng)時(shí),最后一種方法最適合。

一旦你指定一個(gè)EMF模型,EMF生成器就可以創(chuàng)建一個(gè)一致的Java實(shí)現(xiàn)類的集合,你可以編輯生成的類來添加方法與實(shí)例變量,只要需要還可以重新從模型中生成代碼:你添加的部分在重新生成過程中都將被保留。若你添加的代碼依賴于你在模型中修改的某些東西,你還需要更新代碼來反映這些改變,其它情況下,你的代碼是完全不受模型修改與重新生成的影響的。

另外,通過以下方法,就可以簡單地提高你的生產(chǎn)力:使用EMF提供的幾個(gè)其它的益處,如模型變動(dòng)通知、持久化支持(包括默認(rèn)的XMI、以及基于SchemaXML序列化),模型校驗(yàn)框架,以及非常有效的用來操縱EMF對(duì)象的反射API。最重要的是,EMF提供了與其它基于EMF的工具或應(yīng)用進(jìn)行互操作的基礎(chǔ)。

EMF包括兩個(gè)基本的框架,core框架與EMF.Edit。core框架通過為模型創(chuàng)建實(shí)現(xiàn)類,提供基本的代碼生成與運(yùn)行時(shí)支持。EMF.Edit基于core構(gòu)建并進(jìn)行了擴(kuò)展,添加了對(duì)生成適配器類的支持,可以支持視圖以及基于命令的(可以undo的)模型編輯操作。下面的章節(jié)描述core框架的主要功能。EMF.Edit將在另一篇文章中進(jìn)行描述“The EMF.Edit Framework Overview”。指南“Generatin an EMF Model”詳細(xì)介紹了如何運(yùn)行EMFEMF.Edit生成器。

 

EMFOMG MOF的關(guān)系

如果你已經(jīng)熟悉OMGMOF,你肯定會(huì)困惑于EMF倒底與MOF有什么關(guān)系。實(shí)際上,EMF就是從作為MOF規(guī)范的一個(gè)實(shí)現(xiàn)開始的,通過實(shí)現(xiàn)大量使用EMF的工具積累的經(jīng)驗(yàn),我們又對(duì)它進(jìn)行了發(fā)展。EMF可以被看作對(duì)于MOF的部分核心API的一個(gè)高效的Java實(shí)現(xiàn)。然而,為避免任何混淆,與MOF核心元模型類似的部分在EMF中稱為Ecore。

MOF2.0計(jì)劃中,MOF模型的一個(gè)類似子集,稱為(EMOF,Essential MOF),也被分離了出來。在EcoreEMOF間只存在微小的,大部分是命名上的區(qū)別;無論如何,EMF都可以透明地讀寫EMOF的序列化存儲(chǔ)。

 

定義一個(gè)EMF模型

為了有助于描述EMF,我們假定擁有一個(gè)簡單的、只包含一個(gè)類的模型,如下圖:

模型中展示了一個(gè)擁有兩個(gè)屬性的類:String類型的title,int類型的pages

我們的如上圖這么簡單的模型定義,可以通過幾種不同的方式提供給EMF代碼生成器。

UML

若你擁有與EMF一起工作的建模工具,你可以簡單地畫出如上圖所示的模型。

XMI

另外,我們可以直接用XMI文檔來描述這個(gè)模型,就像下面所示:

<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="library "nsURI="http:///library.ecore" nsPrefix="library">

    <eClassifiers xsi:type="ecore:EClass" name="Book">

        <eStructuralFeatures xsi:type="ecore:EAttribute" name="title" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>

        <eStructuralFeatures xsi:type="ecore:EAttribute" name="pages" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>

    </eClassifiers>

</ecore:EPackage>

 

XMI文檔包含了類圖中的所有信息.。在圖中的每個(gè)類與屬性都在XMI文檔中有一個(gè)對(duì)應(yīng)的類或?qū)傩远x。

Annotated Java

如果,你沒有圖形化建模工具,也沒有興趣手工輸入所有的XMI語句,這是第三個(gè)描述你的模型的選項(xiàng)。因?yàn)?/span>EMF生成器是一個(gè)可以并入代碼的生成器,通過提供部分Java接口(將模型信息通過注解提供),生成器可以將接口作為生成模型的元數(shù)據(jù),并將其它的代碼生成到最終實(shí)現(xiàn)代碼中。

我們可以這樣定義Book類:

/**

* @model

*/

public interface Book

{

    **

    * @model

    */

    String getTitle();

   

    /**

    * @model

    */

    int getPages();

}

通過這種方法,用Java接口中標(biāo)準(zhǔn)的get方法來標(biāo)注屬性與引用的形式,我們提供了模型的所有信息。@model標(biāo)簽用來向代碼生成器表示那些接口,以及接口的那些部分對(duì)應(yīng)到模型元素,并需要進(jìn)行相應(yīng)的代碼生成。

對(duì)于我們的簡單示例,所有模型信息都確實(shí)通過對(duì)接口進(jìn)行Java反省操作而提供,所以不再需要任何其它附加的模型信息。在一般情況下,還可以在@model標(biāo)簽后添加模型元素的附加詳細(xì)信息。針對(duì)此示例,若我們希望pages屬性是只讀的(這意味著不生成setter方法),我們就可以向注解中加入信息:

 

/**

* @model changeable="false"

*/

int getPages();

因?yàn)橹挥信c默認(rèn)值不一致的信息才需要被明確指定,所以注解可以保持簡潔明了。

 

XML Schema

有時(shí)候,你可以通過XML Schema描述一個(gè)模型的實(shí)例序列化后看起來應(yīng)該怎樣來定義一個(gè)模型。這對(duì)于編寫一個(gè)使用XML來整合現(xiàn)存的應(yīng)用系統(tǒng)或遵循某個(gè)標(biāo)準(zhǔn)的的應(yīng)用系統(tǒng)是很有幫助的。以下就是與我們的簡單模型等值的Schema文檔:

<xsd:schema targetNamespace="http:///library.ecore" xmlns="http:///library.ecore" lns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:complexType name="Book">   

        <xsd:sequence>

            <xsd:element name="title" type="xsd:string"/>

            <xsd:element name="pages" type="xsd:integer"/>

        </xsd:sequence>

    </xsd:complexType>

</xsd:schema>

這種方法與另外三種有些不同,主要因?yàn)?/span>EMF在最后的使用中,必然會(huì)加上某種序列化的約束,來確保與schema的一致性。作為結(jié)果,從Schema中創(chuàng)建的模型看起來就與使用其它方法生成的模型不一樣。這些區(qū)別的細(xì)節(jié)將在概述后的章節(jié)中討論。

在本文的剩余部分,為保持簡潔明了,我們將統(tǒng)一使用UML圖。所有我們所闡述的建模要領(lǐng)都可以用注解Java接口或直接使用XMI來表示,其中大部分也都存在有等價(jià)的XML Schema。不管提供了什么信息,對(duì)于EMF的代碼生成來說都是一樣的。

 

生成Java實(shí)現(xiàn)

對(duì)于模型中的每個(gè)類,都將會(huì)生成一個(gè)Java接口以及對(duì)應(yīng)的實(shí)現(xiàn)類。在我們例子中,為Book生成的接口如下:

  public interface Book extends EObject

  {

    String getTitle();

    void setTitle(String value);

 

    int getPages();

    void setPages(int value);

  }

每個(gè)生成的接口包含針對(duì)每個(gè)屬性的gettersetter方法。

Book接口擴(kuò)展了基礎(chǔ)接口EObject。EObject是在EMF中等價(jià)于java.lang.Object的對(duì)象,也就是說,它是所有EMF類的基類。EObject以及它的實(shí)現(xiàn)類EObjectImpl(我們將在稍后討論)提供了一些基礎(chǔ)的輕量級(jí)的實(shí)現(xiàn),來為Book引入EMF的通知與持久框架。在我們開始討論EObject到底往混合中帶來了什么之前,讓我們繼續(xù)來看一看EMF如何生成Book。

每個(gè)生成的實(shí)現(xiàn)類都包含定義在對(duì)應(yīng)的接口中的gettersetter方法的實(shí)現(xiàn),還加上其它一些EMF框架所需的方法。

BookImpl將會(huì)包含有titlepages屬性的存取實(shí)現(xiàn)。pages屬性,如例子,擁有如下生成的實(shí)現(xiàn)代碼:

public class BookImpl extends EObjectImpl implements Book

{

    ...

    protected static final int PAGES_EDEFAULT = 0;

    protected int pages = PAGES_EDEFAULT;

 

    public int getPages()

    {

      return pages;

    }

 

    public void setPages(int newPages)

    {

      int oldPages = pages;

      pages = newPages;

      if (eNotificationRequired())

        eNotify(new ENotificationImpl(this, Notification.SET, ..., oldPages, pages));

    }

    ...

}

 

生成的get方法提供了理想的效率。它簡單地返回表示屬性的一個(gè)實(shí)例變量。

set方法,雖然有點(diǎn)復(fù)雜,但還是相當(dāng)高效。在設(shè)置pages實(shí)例變量的值之外,set方法還需要向可能的觀察者發(fā)送改變通知,這些觀察者通過eNotity()來監(jiān)聽對(duì)象。在沒有觀察者時(shí)(例如,在一個(gè)批處理中),情況就可以得到優(yōu)化,通知對(duì)象的構(gòu)造、以及通知方法的調(diào)用,都由eNotificationRequired()方法來進(jìn)行監(jiān)控。eNotificationRequired()方法默認(rèn)的實(shí)現(xiàn)只是簡單地檢查是否存在與對(duì)象相關(guān)的觀察者(或適配器)。因此,若在沒有觀察者的情況下使用EMF對(duì)象,對(duì)eNotificationRequired的調(diào)用與一個(gè)null指針檢查相當(dāng),它可以在JIT編譯器中進(jìn)行Inline處理。

對(duì)于其它類型的屬性自動(dòng)生成的訪問方式,像String類型的title屬性,有一些不同,但基本與上面列出的代碼類似。

對(duì)于引用屬性生成的訪問器,特別是雙向引用,就變得有點(diǎn)復(fù)雜。

單向引用

讓我們用另一個(gè)與Book類有關(guān)聯(lián)關(guān)系的類Writer來擴(kuò)充示例模型。

 BookWriter間的關(guān)聯(lián)關(guān)系是,在本例中,一個(gè)單向的引用。從Book存取Writer的引用(角色)名字為author。

EMF生成器中運(yùn)行此模型,將會(huì)再生成一個(gè)新的接口Writer與實(shí)現(xiàn)類WriterImpl,并在Book接口中添加新的gettersetter方法。

Writer getAuthor();

void setAuthor(Writer value);

因?yàn)?/span>author引用是單向的,所以setAuthor()方法看起來還是一個(gè)簡單的數(shù)據(jù)設(shè)置,與前面的setPages()類似:

public void setAuthor(Writer newAuthor)

{

  Writer oldAuthor = author;

  author = newAuthor;

  if(eNotificationRequired())

    eNotify(new ENotificationImpl(this, ...));

}

其中唯一的區(qū)別就是我們用一個(gè)對(duì)象指針代替了簡單數(shù)據(jù)類型的字段。

因?yàn)槲覀冊(cè)谔幚硪粋€(gè)對(duì)象引用,所以,getAuther()方法會(huì)復(fù)雜一點(diǎn)。因?yàn)閷?duì)于一些類型的引用,包括author類型,需要處理這種可能性:即被引用的對(duì)象(在本例中是Writer)可能被持久化在與源對(duì)象(在本例中是Book)不同的資源(文檔)中。因?yàn)?/span>EMF的持久化框架使用懶惰加裁模式,一個(gè)對(duì)象指針(在本例中是author)在某一時(shí)間點(diǎn)可能只是對(duì)象的一個(gè)代理,而不是真正的引用此對(duì)象。綜上所述,getAuthor()方法如下所示:

public Writer getAuthor()

{

  if (author != null && author.eIsProxy())

  {

    Writer oldAuthor = author;

    author = (Writer)eResolveProxy((InternalEObject)author);

    if (author != oldAuthor)

    {

      if (eNotificationRequired())

        eNotify(new ENotificationImpl(this, Notification.RESOLVE, ...));

    }

  }

  return author;

}

代替簡單地返回author實(shí)例變量,我們首先調(diào)用了從框架中繼承來的方法eIsProxy()來檢查引用是否是一個(gè)代理,若是,再調(diào)用eResolveProxy()來處理它。后者將會(huì)調(diào)用EcoreUtil.resolve(),一個(gè)靜態(tài)工具方法來試圖加載目標(biāo)對(duì)象的文檔,并使用代理URI來構(gòu)建此對(duì)象。若成功,它將返回被解析的對(duì)象,若沒有成功載入文檔,則它還將再次返回代理。

雙向引用

現(xiàn)在我們理解了代理解析對(duì)某些類型的引用的getter方式所起的影響,現(xiàn)在我們?cè)賮砜匆豢措p向關(guān)聯(lián)關(guān)系如何影響setter方式。將單向關(guān)聯(lián)改為如下:

現(xiàn)在關(guān)聯(lián)是雙向的了,通過不帶箭頭的關(guān)聯(lián)線來進(jìn)行表示。從Write訪問Book的角色名為books。

當(dāng)我們?yōu)槟P椭匦律纱a時(shí),getAuthor()方法不會(huì)有改變,但setAuthor()將變?yōu)槿缦滤荆?/span>

public void setAuthor(Writer newAuthor)

{

  if (newAuthor != author)

  {

    NotificationChain msgs = null;

    if (author != null)

      msgs = ((InternalEObject)author).eInverseRemove(this, ..., msgs);

    if (newAuthor != null)

      msgs = ((InternalEObject)newAuthor).eInverseAdd(this, ..., msgs);

    msgs = basicSetAuthor(newAuthor, msgs);

    if (msgs != null) msgs.dispatch();

  }

  else if (eNotificationRequired())

    eNotify(new ENotificationImpl(this, ...)); // send "touch" notification

}

我們可以看到,當(dāng)設(shè)置一個(gè)雙向引用,如author,時(shí),引用的另一端必須也同時(shí)被設(shè)置(通過調(diào)用eInverseAdd())。而且,我們也需要為另一端移除原先的author(通過調(diào)用eInverseRemove()),因?yàn)樵谀P椭校?/span>author引用是單一的(也就是說,一本書只能有一個(gè)作者),Book不能擁有超過一個(gè)Writer引用。最后,我們通過調(diào)用另一個(gè)生成的方法basicSetAuthor()來設(shè)置新的author引用。此方法如下:

public NotificationChain basicSetAuthor(Writer newAuthor, NotificationChain msgs)

{

  Writer oldAuthor = author;

  author = newAuthor;

  if (eNotificationRequired())

  {

    ENotificationImpl notification = new ENotificationImpl(this, ...);

    if (msgs == null) msgs = notification; else msgs.add(notification);

  }

  return msgs;

}

這個(gè)方法與單向引用的set方法非常類似,除非msgs參數(shù)不為空,將會(huì)把notification加入到其中,代替原來直接觸發(fā)此通知消息的做法。因?yàn)樵陔p向引用的set操作中發(fā)生的正向的/反向的添加、以及移除操作,會(huì)有四個(gè)(在本例中是三個(gè))不同的通知消息被生成。NotificationChain被用來集中所有這些單個(gè)的消息,直到所有狀態(tài)改變都完成后再來觸發(fā)它們。隊(duì)列中的消息通過調(diào)用msgs.dispatch()進(jìn)行發(fā)送。

多值引用

可以注意到在示例中books關(guān)聯(lián)(從WriterBook)是多值關(guān)聯(lián)(0..*)。換句話說,一個(gè)作者可能寫有多本書。EMF中的多值引用(上界大于1的引用)使用集合API來處理,所以在接口中只生成了getter方法:

public interface Writer extends EObject

{

  ...

  EList getBooks();

}

注意到getBooks()返回一個(gè)替代java.util.ListEList。實(shí)際上,它們倆基本相同。EListjava.util.ListEMF中的子類,并在API中加入了兩個(gè)move方法。除此之外,從客戶端視角,你可以認(rèn)為它就是一個(gè)JavaList。如,向books關(guān)聯(lián)中添加一本書,只需簡單地調(diào)用:

aWriter.getBooks().add(aBook);

或者,通過迭代器你可以如下所示做其它的事情:

for (Iterator iter = aWriter.getBooks().iterator(); iter.hasNext(); )

{

  Book book = (Book)iter.next();

  ...

}

如上面所示,從客房端視角,操縱多值引用沒有任何特殊之處。然而,因?yàn)?/span>books引用是雙向引用的一部分(它是Book.author的另一端),我們還是要做如同在setAuthor()方法中所示的所有相對(duì)的握手處理。從WriterImpl中的getBooks()方法的實(shí)現(xiàn)代碼可看到如何處理多值引用的情況:

public EList getBooks()

{

  if (books == null)

  {

    books = new EObjectWithInverseResolvingEList(Book.class, this,                  LibraryPackage.WRITER__BOOKS, LibraryPackage.BOOK__AUTHOR);

  }

  return books;

}

getBooks()方法返回一個(gè)特殊的實(shí)現(xiàn)類EObjectWithInverseResolvingEList,通過向它提供為在添加或移除操作時(shí)順利完成相對(duì)的握手處理所需的信息來構(gòu)造它的實(shí)例。EMF實(shí)際上提供了20種不同的特殊的EList實(shí)現(xiàn),來高效地實(shí)現(xiàn)所有類型的多值屬性。對(duì)于單向關(guān)聯(lián)關(guān)系(也就是說,沒有反向)我們可以使用EObjectResolvingElist。若引用操作不需要代理解析,我們可以使用EObjectWithInverseEList或者EObjectEList等等。

所以對(duì)于我們的例子,實(shí)現(xiàn)對(duì)bookd引用的list對(duì)象使用參數(shù)LibraryPackage.BOOK_AUTHOR來進(jìn)行創(chuàng)建(一個(gè)自動(dòng)生成的靜態(tài)int常量,表示相對(duì)的特性)。此參數(shù)在調(diào)用add()方法中對(duì)BookeInverseAdd()方法進(jìn)行調(diào)用時(shí)使用,類似于在BooksetAuthor()期間對(duì)WritereInverseAdd()調(diào)用的方式。下面是在BookImpleInverseAdd() 方法的實(shí)現(xiàn):

public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID,                                       Class baseClass, NotificationChain msgs)

{

  if (featureID >= 0)

  {

    switch (eDerivedStructuralFeatureID(featureID, baseClass))

    {

      case LibraryPackage.BOOK__AUTHOR:

        if (author != null)

          msgs = ((InternalEObject)author).eInverseRemove(this, .., msgs);

        return basicSetAuthor((Writer)otherEnd, msgs);

      default:

        ...

    }

  }

  ...

}

它首先調(diào)用eInverseRemove()方法來移除任何原先的作者(如同BooksetAuthor()方法),然后,它調(diào)用basicSetAuthor()來實(shí)際設(shè)置引用。雖然在我們的例子中,只有一個(gè)雙向引用,在eInverseAdd()中使用了switch結(jié)構(gòu)可以對(duì)Book類的所有雙向引用進(jìn)行處理。

容器引用(復(fù)合聚合)

讓我們?cè)僭黾右粋€(gè)新類,Library,把它作為Book的容器。

容器引用通過在Library這端使用黑心菱形的箭頭線來表示。此關(guān)聯(lián)關(guān)系表示一個(gè)Library聚合,可以有0或更多本書。值聚合(包容)關(guān)聯(lián)關(guān)系是特別重要的因?yàn)樗硎玖艘粋€(gè)目標(biāo)實(shí)例的父實(shí)例,或者稱為擁有者,它也指明了在持久化處理時(shí)對(duì)象的物理位置。

容器從多個(gè)方面影響代碼生成。首先,因?yàn)楸话莸膶?duì)象保證與它的容器處于同一資源中,就不再需要代理解析。因而,在LibraryImpl中生成的get方法將使用一個(gè)不需要解析的EList實(shí)現(xiàn)類:

public EList getBooks()

{

  if (books == null)

  {

    books = new EObjectContainmentEList(Book.class, this, ...);

  }

  return books;

}

因?yàn)椴恍枰瓿纱斫馕?,一個(gè)EObjectContainmentEList可以非常高效地實(shí)現(xiàn)contains()操作(就是說,與普通的線性增長的耗時(shí),可在一個(gè)常量時(shí)間內(nèi)完成操作)。這是非常重要的,因?yàn)樵?/span>EMF引用列表中,不允許進(jìn)行復(fù)制項(xiàng)目的操作,所以在add()操作中會(huì)調(diào)用contains()方法。

因?yàn)橐粋€(gè)對(duì)象只能有一個(gè)容器,添加一個(gè)對(duì)象到容器關(guān)聯(lián)中意味著必須將它從現(xiàn)在所處的容器中移出。例如,添加一本bookLibrarybooks列表中將包括將此book從其它Librarybooks列表中移除的操作。它與那些相對(duì)端的重?cái)?shù)是1的雙向關(guān)聯(lián)關(guān)系沒有任何不同。讓我們假定,若Writer類對(duì)于Book也擁有一個(gè)容器關(guān)聯(lián),稱為ownedBooks。這時(shí),一個(gè)給定的處于某個(gè)WriterownedBooks列表中的book實(shí)例,當(dāng)它被加到一個(gè)Librarybooks引用時(shí),它也將被從Writer的列表中移除。

要高效地實(shí)現(xiàn)此種操作,基類EObjectImol擁有一個(gè)EObject類型的實(shí)例變量(eContainer),用來存儲(chǔ)包容它的容器。作為結(jié)果,一個(gè)容器引用隱含地一定是雙向引用。要從Book訪問Library,你可以寫如下代碼:

EObject container = book.eContainer();

if (container instanceof Library)

  library = (Library)container;

若你想要避免向下造型,你可以將關(guān)聯(lián)明確地改為雙向:

并讓EMF來為你生成一個(gè)良好的類型安全的get方法:

public Library getLibrary()

{

  if (eContainerFeatureID != LibraryPackage.BOOK__LIBRARY) return null;

  return (Library)eContainer;

}

注意到,在明確的get方法中使用從EObjectImpl中繼承的eContainer變量,來代替像前面例子中一樣生成一個(gè)實(shí)例變量。

枚舉屬性

到目前為止,我們已經(jīng)看了EMF如何處理簡單屬性,以及各種類型的引用。另一種公共的屬性類型是枚舉。枚舉屬性使用Java類型安全的enum模式來實(shí)現(xiàn)。

我們?cè)黾右粋€(gè)枚舉屬性,category,到Book類:

重新生成實(shí)現(xiàn)類,Book接口現(xiàn)在包括針對(duì)categorygettersetter

BookCategory getCategory();

void setCategory(BookCategory value);

在生成的接口中,category方法使用了類型安全的枚舉類—BookCategory。它為枚舉值定義了靜態(tài)常量,以及其它的方法,如下:

  public final class BookCategory extends AbstractEnumerator

  {

    public static final int MYSTERY = 0;

    public static final int SCIENCE_FICTION = 1;

    public static final int BIOGRAPHY = 2;

 

    public static final BookCategory MYSTERY_LITERAL =

      new BookCategory(MYSTERY, "Mystery");

    public static final BookCategory SCIENCE_FICTION_LITERAL =

      new BookCategory(SCIENCE_FICTION, "ScienceFiction");

    public static final BookCategory BIOGRAPHY_LITERAL =

      new BookCategory(BIOGRAPHY, "Biography");

 

    public static final List VALUES = Collections.unmodifiableList(...));

 

    public static BookCategory get(String name)

    {

      ...

    }

 

    public static BookCategory get(int value)

    {

      ...

    }

 

    private BookCategory(int value, String name)

    {

      super(value, name);

    }

  }

以下略,因?yàn)樵?/span>JDK5.0中,已提供了類似的Enum實(shí)現(xiàn)。要了解上面的代碼,請(qǐng)參見相關(guān)的文檔。

 

工廠與包

在模型接口與實(shí)現(xiàn)類之外,EMF還至少生成兩個(gè)接口(以及它們的實(shí)現(xiàn)類):一個(gè)工廠與一個(gè)包。

工廠,如同名字的意思,用來創(chuàng)建模型中類的實(shí)例,而包則提供一些靜態(tài)變量(如,生成的方法所使用的特性常量)以及便利方法來存取你模型的元數(shù)據(jù)。

下面是book示例中的工廠接口:

public interface LibraryFactory extends EFactory

{

  LibraryFactory eINSTANCE = new LibraryFactoryImpl();

  Book createBook();

  Writer createWriter();

  Library createLibrary();

 

  LibraryPackage getLibraryPackage();

}

如上所示,生成的工廠為每模型中定義的類提供一個(gè)工廠方法(create),以及訪問模型包的方法,一個(gè)指向自身的靜態(tài)常量單值引用。

LibraryPackage接口提供了對(duì)模型元數(shù)據(jù)進(jìn)行方便的訪問:

  public interface LibraryPackage extends EPackage

  {

    ...

    LibraryPackage eINSTANCE = LibraryPackageImpl.init();

   

    static final int BOOK = 0;

    static final int BOOK__TITLE = 0;

    static final int BOOK__PAGES = 1;

    static final int BOOK__CATEGORY = 2;

    static final int BOOK__AUTHOR = 3;

    ...

   

    static final int WRITER = 1;

    static final int WRITER__NAME = 0;

    ...

   

    EClass getBook();

    EAttribute getBook_Title();

    EAttribute getBook_Pages();

    EAttribute getBook_Category();

    EReference getBook_Author();

 

    ...

  }

如上所示,元數(shù)據(jù)通過兩種形式提供:int型常量,以及Ecore元對(duì)象。int常量提供了傳遞元信息的高效手段。你可以注意到生成的方法在實(shí)現(xiàn)中使用到了這些常量。稍后,當(dāng)我們察看如何實(shí)現(xiàn)EMF適配器時(shí),你將看到這些常量還為處理消息時(shí),判斷什么發(fā)生了改變時(shí),來提供高效的手段。還有,與工廠類似,生成的包接口,也提供了一個(gè)指向自身的單值引用。

生成擁有超類的類

讓我們?cè)?/span>Book模型中來創(chuàng)建一個(gè)子類,SchoolBook,如下:

EMF代碼生成器會(huì)如你所愿地處理單一繼承,生成的接口擴(kuò)展了超類接口。

public interface SchoolBook extends Book

實(shí)現(xiàn)類也擴(kuò)展對(duì)應(yīng)的超實(shí)現(xiàn)類。

public class SchoolBookImpl extends BookImpl implements SchoolBook

Java中,支持接口的多重繼承,但每個(gè)EMF實(shí)現(xiàn)類只能擴(kuò)展其中一個(gè)基類的實(shí)現(xiàn)類。因此,若模型中有多重繼承,我們需要決定將使用哪個(gè)類作為基類,其它的都只被當(dāng)成接口的合并,并在繼承后的實(shí)現(xiàn)類中提供所有的接口實(shí)現(xiàn)。

考慮如下圖所示的模型:

我們讓SchoolBook繼承兩個(gè)類:Book以及Asset。我們標(biāo)志Book類作為實(shí)現(xiàn)類的基類。若我們重新生成代碼,接口SchoolBook將會(huì)擴(kuò)展兩個(gè)接口:

public interface SchoolBook extends Book, Asset

實(shí)現(xiàn)類也與前面相同,只是現(xiàn)在包括了從接口合并進(jìn)來的方法getValue()與方法setValue()

public class SchoolBookImpl extends BookImpl implements SchoolBook

  {

    public float getValue()

    {

      ...

    }

 

    public void setValue(float newValue)

    {

      ...

    }

   

    ...

  }

定制生成的實(shí)現(xiàn)類

你可以向生成的Java類中添加行衛(wèi)(方法或?qū)嵗兞浚挥脫?dān)心一旦模型發(fā)生變動(dòng)后重新生成代碼會(huì)搞丟你加入的東西。例如,我們向類Book加入一個(gè)方法,isRecommended()。只要在Java接口Book中簡單地加入新方法的聲明即可。

public interface Book ...

{

  boolean isRecommended();

  ...

}

以及它們?cè)趯?shí)現(xiàn)類中的實(shí)現(xiàn):

public boolean isRecommended()

{

  return getAuthor().getName().equals("William Shakespeare");

}

EMF生成器不會(huì)擦去這些修改,因?yàn)樗皇且粋€(gè)自動(dòng)生成的方法。每個(gè)EMF生成的方法都包括一個(gè)包含@generated標(biāo)簽的Javadoc注解,如下:

/**

 * ...

 * @generated

 */

public String getTitle()

{

  return title;

}

不管怎樣重新生成代碼,EMF都不會(huì)去碰所有不包含此標(biāo)簽的方法(如isRecommended()方法)。實(shí)際上,若我們想要修改一個(gè)自動(dòng)生成的方法,我們可以從它的注解中移除@generated標(biāo)簽。

/**

 * ...

 * @generated // (removed)

 */

public String getTitle()

{

  // our custom implementation ...

}

現(xiàn)在,因?yàn)闆]有@generated標(biāo)簽,getTitle()方法被認(rèn)為了用戶的代碼;若我們重新生成代碼,生成器將會(huì)檢測(cè)到?jīng)_突,并簡單地放棄為此方法生成代碼。

實(shí)際上,在放棄一個(gè)生成的方法前,生成器首先檢查,在文件中是否存在另一個(gè)相同名字的,但加上Gen的,自動(dòng)生成的方法。若它找到一個(gè),則將會(huì)把生成的代碼重定向到這個(gè)方法中。例如,若我們想擴(kuò)展生成的getTitle()方法實(shí)現(xiàn),來代替完全地放棄它,我們可以簡單地改一下方法名字:

/**

 * ...

 * @generated

 */

public String getTitleGen()

{

  return title;

}

并加入我們自己的覆蓋方法:

public String getTitle()

{

  String result = getTitleGen();

  if (result == null)

    result = ...

  return result;

}

現(xiàn)在我們重新生成代碼,生成器將會(huì)檢測(cè)到與我們自己的getTitle()有沖突,但因?yàn)樵陬愔写嬖趲?/span>@generated標(biāo)簽的getTitleGen()方法,它將會(huì)重定向新生成的代碼到此方法中,而不是簡單地放棄生成新的代碼。

EMF模型上的操作

除了屬性與引用,你可以向模型中的類添加操作。若你這么做,則EMF生成器將會(huì)在接口中生成方法的聲明,在實(shí)現(xiàn)類中生成一個(gè)方法框架。EMF不對(duì)行衛(wèi)建模,所以實(shí)現(xiàn)要由用戶自己寫Java代碼來提供。

可以像上面說的,移除@generated標(biāo)簽,然后加入自己的代碼。另處,也可以將Java代碼包括在模型中間。在Rose上,

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
深入淺出Eclipse Modeling Framework (EMF)
eclipse EMF介紹系列(EMF與MDA)
The Eclipse Modeling Framework (EMF) Overview
淺析Eclipse建??蚣?EMF)及其動(dòng)態(tài)能力 中國站長網(wǎng)絡(luò)學(xué)院
Eclipse工程中,展示出UML結(jié)構(gòu)
使用 EMF 進(jìn)行元建模:生成具體、可重用的 Java 代碼片段
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服