日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

GEF 框架中的設(shè)計模式

 gyb98 2010-12-11
邵 兵, 研究員, IBM 中國研究院
石 立川, 軟件工程師, IBM 中國研究院
王 晗, IBM 實習(xí)生, IBM 中國研究院

 

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

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

1 star2 stars3 stars4 stars5 stars 平均分 (共 6 個評分 )

前言

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

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

  • 模型-視圖-控制器 (Model-View-Controller):MVC 模式被 GEF 用來解除用戶界面,行為和表示之間的耦合。模型可以用任意 Java 對象表示,EMF (Eclipse Modeling Framework,Eclipse 建??蚣?) 被普遍使用來構(gòu)造 GEF 的模型。視圖必須實現(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)
圖 1. gef 中的模型-視圖-控制器 (images/gef_mvc_pattern.jpg)

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

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

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


命令 (Command)

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

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

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

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

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


工廠 (Factory)

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

  1. 簡單工廠 (Simple Factory):又叫做靜態(tài)工廠方法 (Static Factory Method) 模式,但不屬于 23 種 GOF 設(shè)計模式。簡單工廠模式由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實例。
  2. 工廠方法模式 (Factory Method):定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類。將創(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)
圖 3. abstractgraphicaleditpart 類圖 (images/factory_method.jpg)

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


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

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


觀察者 (Observer)

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

通過選項欄創(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 類型的實例,用來觀察源 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 類型的實例 deactivationListener 扮演了觀察者的角色,在目標(biāo) EditPart 的狀態(tài)變成非激活時,獲取更新并通知 ConnectionCreationTool 取消連接的創(chuàng)建。


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

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

在 GEF 中,Tools 或者其他的 UI 解釋程序?qū)⒂脩舻木庉嫴僮鬓D(zhuǎn)換為一系列的請求 (Request),比如,用戶在選項板 (Palette) 里選擇了創(chuàng)建節(jié)點工具 (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)實現(xiàn)的 Request.


圖 5. Request 子類 (images/request_type_hierarchy.jpg)
圖 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 實現(xiàn)共享。EditPolicies 決定了一個 EditPart 的編輯能力。

EditPart 在創(chuàng)建時,調(diào)用方法 createEditPolicies() 來安裝一些適用的編輯策略。在示例代碼中,ConnectionEditPart 安裝了兩個 EditPolicy。第一個是 ConnectionComponentPolicy,它給 Delete 菜單項所需要的 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é)鏈進行處理。從第一個 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)對象,在運行時根據(jù)狀態(tài)改變它的行為。關(guān)于狀態(tài)模式更詳細(xì)的描述,請參考 GOF 《設(shè)計模式》一書。

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

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

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


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

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

示例代碼描述了 EditDomain 是如何將與視圖輸入相關(guān)的請求委托給它的 Tool 實例 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è)計思想與大家分享。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多