国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

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

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

 

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

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

平均分 (共 6 個評分 )

前言

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

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

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

本文示例代碼來自于 GEF 的 3.4.1 版本。


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

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


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

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

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

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


命令 (Command)

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

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

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

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

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


工廠 (Factory)

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

  1. 簡單工廠 (Simple Factory):又叫做靜態(tài)工廠方法 (Static Factory Method) 模式,但不屬于 23 種 GOF 設(shè)計(jì)模式。簡單工廠模式由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。
  2. 工廠方法模式 (Factory Method):定義一個用于創(chuàng)建對象的接口,讓子類決定實(shí)例化哪一個類。將創(chuàng)建工作推遲到工廠角色的子類去完成。
  3. 抽象工廠模式 (Abstract Factory):聲明一個抽象的工廠接口,工廠的多個子類分別創(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í)例對象的創(chuàng)建則運(yùn)用了抽象工廠模式。所有的 EditPart 均由 EditPartFactory的子類負(fù)責(zé)創(chuàng)建,GEF 自身就提供了 RulerEditPartFactory 和 PaletteEditPartFactory兩個工廠實(shí)現(xiàn)。如果用戶自定義 EditPart,必須提供相應(yīng)的 EditPartFactory 類型才能正確創(chuàng)建用戶的 EditPart 對象。


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

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


觀察者 (Observer)

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

通過選項(xiàng)欄創(chuàng)建連接時,ConnectionCreationTool工具被生成用來創(chuàng)建連接,它通過監(jiān)聽鼠標(biāo)的按下動作來設(shè)置連接的連接源,在設(shè)置連接源時,通過 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í)例,用來觀察源 EditPart的狀態(tài)。當(dāng)源 EditPart 的狀態(tài)由激活變?yōu)榉羌せ顣r,及時通知工具 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);            }            }            

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


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

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

在 GEF 中,Tools 或者其他的 UI 解釋程序?qū)⒂脩舻木庉嫴僮鬓D(zhuǎn)換為一系列的請求 (Request),比如,用戶在選項(xiàng)板(Palette) 里選擇了創(chuàng)建節(jié)點(diǎn)工具 (CreationTool),然后在畫布區(qū)域按下鼠標(biāo)左鍵,這時產(chǎn)生在畫布上的鼠標(biāo)單擊事件將被CreationTool 轉(zhuǎn)換為一個 CreateRequest,它里面包含了要創(chuàng)建的對象,坐標(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,而是通過安裝的對應(yīng) EditPolicy來處理。EditPolicy 的主要功能是根據(jù)請求創(chuàng)建相應(yīng)的命令 (Command),而后者會直接操作模型對象。每個 EditPolicy專注于一個單一的編輯任務(wù)或相關(guān)任務(wù)組,這使得一些編輯操作可以在不同 EditPart 實(shí)現(xiàn)共享。EditPolicies 決定了一個EditPart 的編輯能力。

EditPart 在創(chuàng)建時,調(diào)用方法 createEditPolicies()來安裝一些適用的編輯策略。在示例代碼中,ConnectionEditPart 安裝了兩個 EditPolicy。第一個是ConnectionComponentPolicy,它給 Delete 菜單項(xiàng)所需要的 action 提供刪除命令。第二個是ConnectionEndpointEditPolicy,用來提供連接 (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());            }    ...            }            

請求提交到選定的 editpart 后,通過 EditPolicy 的職責(zé)鏈進(jìn)行處理。從第一個 EditPolicy開始,鏈中收到請求的 EditPolicy 確定是否可以處理它,否則轉(zhuǎn)發(fā)給鏈中的下一個 editpolicy。EditPolicy的聲明順序決定了請求被傳遞的順序。多個編輯策略可以收到請求,返回 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) 也是一種對象行為型模式。狀態(tài)模式允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為。上下文(context)把狀態(tài)相關(guān)的行為委托到狀態(tài)對象上。對象通過上下文引用不同的狀態(tài)對象,在運(yùn)行時根據(jù)狀態(tài)改變它的行為。關(guān)于狀態(tài)模式更詳細(xì)的描述,請參考 GOF《設(shè)計(jì)模式》一書。

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

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

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


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

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

示例代碼描述了 EditDomain 是如何將與視圖輸入相關(guān)的請求委托給它的 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();            }            }            }            

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

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

后記

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


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

聯(lián)系客服