免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

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

開通VIP
GEF 框架中的設(shè)計(jì)模式
邵 兵, 研究員, IBM 中國(guó)研究院
石 立川, 軟件工程師, IBM 中國(guó)研究院
王 晗, IBM 實(shí)習(xí)生, IBM 中國(guó)研究院

 

簡(jiǎn)介: 本文從設(shè)計(jì)模式的角度出發(fā),通過(guò)解析關(guān)鍵應(yīng)用場(chǎng)景,深層次地介紹了圖形編輯框架 (Graphical EditingFramework, GEF) 涉及的大量概念和技術(shù)。本文主要涉及 MVC、命令、工廠、觀察者、職責(zé)鏈、狀態(tài)等模式。通過(guò)本文,希望能夠幫助Eclipse RCP 開發(fā)者更好地理解和應(yīng)用 GEF 這一框架。

發(fā)布日期: 2010 年 6 月 03 日
級(jí)別: 中級(jí)
訪問(wèn)情況 240 次瀏覽
建議: 0 (添加評(píng)論)

平均分 (共 6 個(gè)評(píng)分 )

前言

圖形編輯框架 (Graphical Editing Framework, GEF) ,是 Eclipse平臺(tái)下一個(gè)重要的框架,用來(lái)從應(yīng)用模型開發(fā)富圖形化的編輯器,是 Eclipse RCP 開發(fā)者的神兵利器。 GEF框架涉及大量的概念和技術(shù),有著非常陡峭的學(xué)習(xí)曲線。本文從設(shè)計(jì)模式的角度出發(fā),解析 GEF 框架中的關(guān)鍵應(yīng)用場(chǎng)景,希望能夠幫助 EclipseRCP 開發(fā)者更好地理解和應(yīng)用這一框架。

GEF 通過(guò)大量使用設(shè)計(jì)模式來(lái)獲取它的靈活性。除了 MVC 模式,GEF 最經(jīng)常用到的設(shè)計(jì)模式是命令、工廠、觀察者、職責(zé)鏈和狀態(tài)。

  • 模型-視圖-控制器 (Model-View-Controller):MVC 模式被 GEF 用來(lái)解除用戶界面,行為和表示之間的耦合。模型可以用任意 Java 對(duì)象表示,EMF (Eclipse Modeling Framework,Eclipse 建??蚣?) 被普遍使用來(lái)構(gòu)造 GEF 的模型。視圖必須實(shí)現(xiàn) IFigure 接口,控制器則必須是 EditPart 類型。
  • 命令 (Command):用來(lái)封裝對(duì)模型的修改,支持 redo、undo 操作。
  • 工廠 (Factory): GEF 框架應(yīng)用工廠方法模式創(chuàng)建 Figure 應(yīng)用抽象工廠模式創(chuàng)建 EditPart。
  • 觀察者 (Observer):通過(guò)觀察 EditPart 的激活狀態(tài),ConnectionCreationTool 修改待創(chuàng)建連接的連接源。
  • 職責(zé)鏈 (Chain of Responsibility):用來(lái)決定請(qǐng)求 (Request) 如何被編輯策略 (EditPolicy) 處理。
  • 狀態(tài) (State):對(duì)于鍵盤、鼠標(biāo)輸入,GEF 編輯器通過(guò) Tool 的改變來(lái)改變自己的行為。

本文示例代碼來(lái)自于 GEF 的 3.4.1 版本。


模型-視圖-控制器 (Model-view-controller, MVC)

GEF 框架嚴(yán)格遵循模型-視圖-控制器模式 (MVC) 。


圖 1. GEF 中的模型-視圖-控制器 (images/gef_mvc_pattern.jpg)

GEF 中的模型可以是任意的數(shù)據(jù)。模型使用一種能在模型改變時(shí)通知控制器處理的事件通知機(jī)制。這種模型可以由手工來(lái)實(shí)現(xiàn),也可以通過(guò) EMF(Eclipse Modeling Framework) 自動(dòng)生成。而對(duì)模型的修改一般由 Command 來(lái)完成。

EditParViewer 是 GEF 中的展現(xiàn)視圖的地方。常見的 EditParViewer 有兩種:GraphicalViewer 和 TreeViewer。GraphicalViewer 主要依靠 Draw2d 中的 Figure來(lái)完成的。開發(fā)人員可以通過(guò)實(shí)現(xiàn) IFigure 接口來(lái)完成復(fù)雜圖形的設(shè)計(jì)。對(duì)于 TreeViewer 而言,則由 SWT 中的 Tree 和TreeItem 來(lái)完成視圖的繪制。

EditPart 對(duì)應(yīng) MVC 模式中的控制器,它維護(hù)著視圖與模型的對(duì)應(yīng)關(guān)系。在 AbstractGraphicalEditPart中,createFigure 方法負(fù)責(zé)創(chuàng)建 Figure 圖形,refreshVisuals 方法負(fù)責(zé)對(duì) Figure圖形進(jìn)行更新。一般情況下,模型與 EditPart 是一一對(duì)應(yīng)的。模型數(shù)據(jù)的更新由 EditPart 所安裝的編輯策略產(chǎn)生的 Command來(lái)完成。GEF 框架中的常見的 EditPart 實(shí)現(xiàn)有三種,分別是 GraphicalEditPart,ConnecitonEditPart 和TreeEditPart。


命令 (Command)

GEF 不會(huì)直接修改模型,而是要求使用命令來(lái)做實(shí)際的修改。通過(guò)命令,實(shí)現(xiàn)對(duì)模型或模型屬性的修改和撤銷。這樣,GEF 編輯器就自動(dòng)支持了模型修改的 undo/redo。

Command 類是 GEF 中的一個(gè)抽象類,主要實(shí)現(xiàn)如下幾個(gè)方法:

  • execute:這是命令的執(zhí)行方法,當(dāng)請(qǐng)求結(jié)束并獲得 Command 后,GEF 框架 ( 通過(guò) CommandStack) 負(fù)責(zé)執(zhí)行此方法。
  • undo:對(duì)模型修改后,可以通過(guò) undo 進(jìn)行撤銷。
  • redo:當(dāng)用戶撤銷后,能通過(guò) redo 重復(fù)上一次的操作。

圖 2. Command 相關(guān)類圖 (images/command.jpg)

每個(gè)編輯策略都會(huì)為請(qǐng)求返回一個(gè)命令,不希望處理請(qǐng)求的策略將返回一個(gè) null。GEF 通過(guò)一個(gè)命令堆棧 (CommandStack) 執(zhí)行和保存 Command 對(duì)象。用戶通過(guò)命令堆??梢暂p松撤銷或重復(fù)對(duì)模型所做的操作。


工廠 (Factory)

工廠模式是用于將生成對(duì)象的步驟進(jìn)行封裝的創(chuàng)建型模式。常見的形態(tài)有以下幾種:

  1. 簡(jiǎn)單工廠 (Simple Factory):又叫做靜態(tài)工廠方法 (Static Factory Method) 模式,但不屬于 23 種 GOF 設(shè)計(jì)模式。簡(jiǎn)單工廠模式由一個(gè)工廠對(duì)象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。
  2. 工廠方法模式 (Factory Method):定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。將創(chuàng)建工作推遲到工廠角色的子類去完成。
  3. 抽象工廠模式 (Abstract Factory):聲明一個(gè)抽象的工廠接口,工廠的多個(gè)子類分別創(chuàng)建某一系列的產(chǎn)品。

在 GEF 中,F(xiàn)igure 的創(chuàng)建應(yīng)用了工廠方法模式。抽象類 AbstractGraphicalEditPart擔(dān)當(dāng)抽象工廠角色,定義了生成 Figure 的抽象方法 createFigure()。具體工廠角色則有AbstractGraphicalEditPart 的子類擔(dān)當(dāng),負(fù)責(zé)生成具體的編輯器圖形。


圖 3. AbstractGraphicalEditPart 類圖 (images/factory_method.jpg)

EditPart 實(shí)例對(duì)象的創(chuàng)建則運(yùn)用了抽象工廠模式。所有的 EditPart 均由 EditPartFactory的子類負(fù)責(zé)創(chuàng)建,GEF 自身就提供了 RulerEditPartFactory 和 PaletteEditPartFactory兩個(gè)工廠實(shí)現(xiàn)。如果用戶自定義 EditPart,必須提供相應(yīng)的 EditPartFactory 類型才能正確創(chuàng)建用戶的 EditPart 對(duì)象。


圖 4. EditPartFactory 相關(guān)類圖 (images/EditPartFactory.jpg)

工廠方法和抽象工廠之間的區(qū)別在于,工廠方法模式只有一個(gè)抽象產(chǎn)品類,而抽象工廠模式有多個(gè)。工廠方法模式的具體工廠類只能創(chuàng)建一個(gè)具體產(chǎn)品類的實(shí)例,而抽象工廠模式可以創(chuàng)建多個(gè)。


觀察者 (Observer)

觀察者模式是一種對(duì)象行為型模式,它可以定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)被依賴對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。

通過(guò)選項(xiàng)欄創(chuàng)建連接時(shí),ConnectionCreationTool工具被生成用來(lái)創(chuàng)建連接,它通過(guò)監(jiān)聽鼠標(biāo)的按下動(dòng)作來(lái)設(shè)置連接的連接源,在設(shè)置連接源時(shí),通過(guò) addEditPartListener 方法為源EditPart 添加 deactivationListener 監(jiān)聽。


清單 1. AbstractConnectionCreationTool 類中部分代碼
            package org.eclipse.gef.tools;            public class AbstractConnectionCreationTool            extends TargetingTool{            ......            private EditPartListener.Stub deactivationListener = new EditPartListener.Stub() {            public void partDeactivated(EditPart editpart) {            handleSourceDeactivated();            }            };            protected boolean handleButtonDown(int button) {            if (isInState(STATE_INITIAL) && button == 1) {            updateTargetRequest();            updateTargetUnderMouse();            setConnectionSource(getTargetEditPart());            Command command = getCommand();            ((CreateConnectionRequest)getTargetRequest()).setSourceEditPart(            getTargetEditPart());            if (command != null) {            setState(STATE_CONNECTION_STARTED);            setCurrentCommand(command);            viewer = getCurrentViewer();            }            }            if (isInState(STATE_INITIAL) && button != 1) {            setState(STATE_INVALID);            handleInvalidInput();            }            return true;            }            protected void setConnectionSource(EditPart source) {            if (connectionSource != null)            connectionSource.removeEditPartListener(deactivationListener);            connectionSource = source;            if (connectionSource != null)            connectionSource.addEditPartListener(deactivationListener);            }            protected void handleSourceDeactivated() {            setState(STATE_INVALID);            handleInvalidInput();            handleFinished();            }            }            

deactivationListener 是 EditPartListener.Stub 類型的實(shí)例,用來(lái)觀察源 EditPart的狀態(tài)。當(dāng)源 EditPart 的狀態(tài)由激活變?yōu)榉羌せ顣r(shí),及時(shí)通知工具 ConnectionCreationTool 做出停止創(chuàng)建連接的工作。


清單 2. AbstractEditPart 類中部分代碼
            package org.eclipse.gef.editparts;            public abstract class AbstractEditPart            implements EditPart, RequestConstants, IAdaptable {            ... ...            protected void fireDeactivated() {            Iterator listeners = getEventListeners(EditPartListener.class);            while (listeners.hasNext())            ((EditPartListener)listeners.next()).            partDeactivated(this);            }            /**            * Adds an EditPartListener.            * @param listener the listener            */            public void addEditPartListener(EditPartListener listener) {            eventListeners.addListener(EditPartListener.class, listener);            }            public void removeEditPartListener(EditPartListener listener) {            eventListeners.removeListener(EditPartListener.class, listener);            }            }            

在這個(gè)過(guò)程中,EditPart 成為被觀察的目標(biāo),提供了注冊(cè)和刪除觀察者對(duì)象的接口。EditPartListener.Stub類型的實(shí)例 deactivationListener 扮演了觀察者的角色,在目標(biāo) EditPart 的狀態(tài)變成非激活時(shí),獲取更新并通知ConnectionCreationTool 取消連接的創(chuàng)建。


職責(zé)鏈 (Chain of Responsibility)

職責(zé)鏈?zhǔn)且环N對(duì)象行為型模式。請(qǐng)求發(fā)出后,將在候選對(duì)象鏈 ( 職責(zé)鏈 )中進(jìn)行傳遞,并有滿足條件的對(duì)象進(jìn)行處理。職責(zé)鏈模式降低了請(qǐng)求的發(fā)送者和接收者之間的耦合度,允許在運(yùn)行時(shí)對(duì)職責(zé)鏈進(jìn)行動(dòng)態(tài)的增加或修改以增加或改變處理請(qǐng)求的職責(zé)。關(guān)于職責(zé)鏈模式更詳細(xì)的描述,請(qǐng)參考 GOF 《設(shè)計(jì)模式》一書。

在 GEF 中,Tools 或者其他的 UI 解釋程序?qū)⒂脩舻木庉嫴僮鬓D(zhuǎn)換為一系列的請(qǐng)求 (Request),比如,用戶在選項(xiàng)板(Palette) 里選擇了創(chuàng)建節(jié)點(diǎn)工具 (CreationTool),然后在畫布區(qū)域按下鼠標(biāo)左鍵,這時(shí)產(chǎn)生在畫布上的鼠標(biāo)單擊事件將被CreationTool 轉(zhuǎn)換為一個(gè) CreateRequest,它里面包含了要?jiǎng)?chuàng)建的對(duì)象,坐標(biāo)位置等信息。

GEF 已經(jīng)為我們提供了很多種類的 Request,其中最常用的是 CreateRequest 及其子類 CreateConnectionRequest,下圖列出了 GEF 中已經(jīng)實(shí)現(xiàn)的 Request.


圖 5. Request 子類 (images/request_type_hierarchy.jpg)

Editparts 不能直接處理編輯操作產(chǎn)生的 Request,而是通過(guò)安裝的對(duì)應(yīng) EditPolicy來(lái)處理。EditPolicy 的主要功能是根據(jù)請(qǐng)求創(chuàng)建相應(yīng)的命令 (Command),而后者會(huì)直接操作模型對(duì)象。每個(gè) EditPolicy專注于一個(gè)單一的編輯任務(wù)或相關(guān)任務(wù)組,這使得一些編輯操作可以在不同 EditPart 實(shí)現(xiàn)共享。EditPolicies 決定了一個(gè)EditPart 的編輯能力。

EditPart 在創(chuàng)建時(shí),調(diào)用方法 createEditPolicies()來(lái)安裝一些適用的編輯策略。在示例代碼中,ConnectionEditPart 安裝了兩個(gè) EditPolicy。第一個(gè)是ConnectionComponentPolicy,它給 Delete 菜單項(xiàng)所需要的 action 提供刪除命令。第二個(gè)是ConnectionEndpointEditPolicy,用來(lái)提供連接 (Connection) 轉(zhuǎn)移的策略。


清單 3. ConnectionEditPart 類中部分代碼
            class ConnectionEditPart extends AbstractConnectionEditPart            implements PropertyChangeListener {            ...            protected void createEditPolicies() {            installEditPolicy(EditPolicy.CONNECTION_ROLE, new ConnectionEditPolicy() {            protected Command getDeleteCommand(GroupRequest request) {            return new ConnectionDeleteCommand(getCastedModel());            }            });            installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE,            new ConnectionEndpointEditPolicy());            }    ...            }            

請(qǐng)求提交到選定的 editpart 后,通過(guò) EditPolicy 的職責(zé)鏈進(jìn)行處理。從第一個(gè) EditPolicy開始,鏈中收到請(qǐng)求的 EditPolicy 確定是否可以處理它,否則轉(zhuǎn)發(fā)給鏈中的下一個(gè) editpolicy。EditPolicy的聲明順序決定了請(qǐng)求被傳遞的順序。多個(gè)編輯策略可以收到請(qǐng)求,返回 Commands 作為響應(yīng),這些 Commands以鏈的方式組織在一起。示例代碼描述了 AbstractEditPart 中的職責(zé)鏈工作方式。


清單 4. AbstractEditPart 類中部分代碼
            package org.eclipse.gef.editparts;            public abstract class AbstractEditPart            implements EditPart, RequestConstants, IAdaptable            {            ......            public Command getCommand(Request request) {            Command command = null;            EditPolicyIterator i = getEditPolicyIterator();            while (i.hasNext()) {            if (command != null)            command = command.chain(i.next().getCommand(request));            else            command = i.next().getCommand(request);            }            return command;            }            }            


狀態(tài) (State)

同職責(zé)鏈模式一樣,狀態(tài) (State) 也是一種對(duì)象行為型模式。狀態(tài)模式允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。上下文(context)把狀態(tài)相關(guān)的行為委托到狀態(tài)對(duì)象上。對(duì)象通過(guò)上下文引用不同的狀態(tài)對(duì)象,在運(yùn)行時(shí)根據(jù)狀態(tài)改變它的行為。關(guān)于狀態(tài)模式更詳細(xì)的描述,請(qǐng)參考 GOF《設(shè)計(jì)模式》一書。

在 GEF 的編輯器中,用戶在選項(xiàng)板 (Palette)切換工具可以改變編輯器的狀態(tài),從而修改編輯器的行為。例如,對(duì)于鼠標(biāo)按下事件,編輯器在激活選區(qū)工具和激活創(chuàng)建工具下的行為是截然不同的?,F(xiàn)在,我們就來(lái)看一下 GEF 編輯器是如何根據(jù)當(dāng)前選中的 Tool 來(lái)改變行為的。

在每個(gè) GEF 的 Editor 里,都需要有一個(gè) EditDomain 的存在。EditDomain 類似于GraphicalEditor 的執(zhí)行上下文環(huán)境,維護(hù)著 GEF 中的命令棧、負(fù)責(zé)事件通知等。在 EditDomain 中,通過(guò)setActiveTool 可以設(shè)置當(dāng)前處于 Active 狀態(tài)的 Tool。

EditDomain 類維護(hù)一個(gè)表示鼠標(biāo)和鍵盤輸入的工具對(duì)象 ( 一個(gè) Tool 接口實(shí)現(xiàn)類的實(shí)例 )。EditDomain類將所有與視圖輸入相關(guān)的請(qǐng)求委托給這個(gè)工具對(duì)象。EditDomain 類使用 Tool接口實(shí)現(xiàn)類的實(shí)例來(lái)執(zhí)行特定于視圖輸入的操作。在狀態(tài)模式中,EditDomain 對(duì)應(yīng)上下文環(huán)境,工具 (Tool) 對(duì)應(yīng)狀態(tài)。一旦 ActiveTool 改變,EditDomain 對(duì)象就會(huì)改變它所使用的工具對(duì)象。


圖 6. Tool 繼承層次圖 (images/tool_hierarchy.jpg)

需要注意的是,上圖關(guān)于 Tool 的繼承層次部分并不是嚴(yán)格按照 GEF 框架進(jìn)行描述,本文作者為了描述方便做了某種程度的簡(jiǎn)化。具體層次請(qǐng)參考 GEF 框架代碼。

示例代碼描述了 EditDomain 是如何將與視圖輸入相關(guān)的請(qǐng)求委托給它的 Tool 實(shí)例 activeTool。


清單 5. EditDomain 類中部分代碼
            package org.eclipse.gef;            public class EditDomain {            ......            private Tool activeTool;            private void handlePaletteToolChanged() {            PaletteViewer paletteViewer = getPaletteViewer();            if (paletteViewer != null) {            ToolEntry entry = paletteViewer.getActiveTool();            if (entry != null)            setActiveTool(entry.createTool());            else            setActiveTool(getDefaultTool());            }            }            public void setActiveTool(Tool tool) {            if (activeTool != null)            activeTool.deactivate();            activeTool = tool;            if (activeTool != null) {            activeTool.setEditDomain(this);            activeTool.activate();            }            }            }            

工具會(huì)執(zhí)行某些操作,這些操作可能包括:

  • 要求 editparts 顯示或隱藏反饋
  • 從 editparts 獲得命令
  • 在命令棧執(zhí)行命令
  • 更新鼠標(biāo)光標(biāo)

后記

GEF 出現(xiàn)的模式遠(yuǎn)不止我們列出來(lái)的這么多。我們只是列出了對(duì) GEF 框架理解有幫助的一些模式。本文作者從事 Eclipse RCP 開發(fā)多年,通過(guò)這篇文章,希望能將在 GEF 中體會(huì)到的一些設(shè)計(jì)思想與大家分享。


本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
【插件開發(fā)】
[PHP小課堂]PHP設(shè)計(jì)模式之簡(jiǎn)單工廠模式
MVC模式學(xué)習(xí)
設(shè)計(jì)模式圖解學(xué)習(xí)(23種)
創(chuàng)建型設(shè)計(jì)模式總結(jié)
設(shè)計(jì)模式分類
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服