JHotDraw讓你成為程序設(shè)計(jì)的畢加索 ——使用可高度定制化的GUI框架來簡化圖形應(yīng)用程序的開發(fā) 軟件開發(fā)人員總是希望能又快又好的進(jìn)行應(yīng)用軟件的開發(fā)。而使用應(yīng)用框架正是一種能在減少開發(fā)時(shí)間的同時(shí)又能提高軟件質(zhì)量的方法。應(yīng)用框架被設(shè)計(jì)用于重用;它們能夠提供預(yù)制的組件作為你所要開發(fā)的系統(tǒng)的構(gòu)件,并提供設(shè)計(jì)模式作為系統(tǒng)架構(gòu)的藍(lán)圖。 很多Java程序員都在頻繁的使用某些框架,不過他們自己可能并沒有意識(shí)到這一點(diǎn)。JFCSwing可以被看作是一種用于生成通用的GUI程序的簡單框架。雖然Swing可以在許多應(yīng)用中派上用場,但是對(duì)于那些基于GUI的應(yīng)用程序來說,它缺乏清楚的結(jié)構(gòu)。在這方面更專業(yè)的一個(gè)框架是JHotDraw,它主要用于繪制那些技術(shù)類和結(jié)構(gòu)化的圖像方面的程序。這類圖像包括網(wǎng)絡(luò)布局圖和pert圖。而且JHot.....
JHotDraw讓你成為程序設(shè)計(jì)的畢加索
——使用可高度定制化的GUI框架來簡化圖形應(yīng)用程序的開發(fā)
軟件開發(fā)人員總是希望能又快又好的進(jìn)行應(yīng)用軟件的開發(fā)。而使用應(yīng)用框架正是一種能在減少開發(fā)時(shí)間的同時(shí)又能提高軟件質(zhì)量的方法。應(yīng)用框架被設(shè)計(jì)用于重用;它們能夠提供預(yù)制的組件作為你所要開發(fā)的系統(tǒng)的構(gòu)件,并提供設(shè)計(jì)模式作為系統(tǒng)架構(gòu)的藍(lán)圖。
很多Java程序員都在頻繁的使用某些框架,不過他們自己可能并沒有意識(shí)到這一點(diǎn)。JFC Swing可以被看作是一種用于生成通用的GUI程序的簡單框架。雖然Swing可以在許多應(yīng)用中派上用場,但是對(duì)于那些基于GUI的應(yīng)用程序來說,它缺乏清楚的結(jié)構(gòu)。在這方面更專業(yè)的一個(gè)框架是JHotDraw,它主要用于繪制那些技術(shù)類和結(jié)構(gòu)化的圖像方面的程序。這類圖像包括網(wǎng)絡(luò)布局圖和pert圖。而且JHotDraw為開發(fā)這些圖像的編輯器提供了很好的支持。在這個(gè)應(yīng)用領(lǐng)域,JHotDraw證明了應(yīng)用框架的威力和用處。在具體的討論JHotDraw之前,我們首先來談?wù)撓驴蚣芎推潴w現(xiàn)的概念。
設(shè)計(jì)模式和框架的理論
當(dāng)開發(fā)者使用框架的時(shí)候,他們并不是僅僅重用代碼,而且重用了一個(gè)原型程序的設(shè)計(jì)和架構(gòu)。通常,框架需要經(jīng)過一些調(diào)整來滿足問題領(lǐng)域的需要。框架并不像代碼庫一樣僅僅只是提供組件,它還提供了用于組合這些組件的結(jié)構(gòu)、組件之間的相互操作的方式,還常常包括應(yīng)用程序的基本結(jié)構(gòu)。這種結(jié)構(gòu)并不像從類庫中得到的那些結(jié)構(gòu)那樣被動(dòng),通過調(diào)用一些用戶定義的組件,它常??梢灶嵏部刂普麄€(gè)控制流程。
開發(fā)者在軟件設(shè)計(jì)時(shí)經(jīng)常面對(duì)的問題常常是那些在某些典型的特定情況下重復(fù)出現(xiàn)的問題。使用設(shè)計(jì)模式,就是在面對(duì)這些問題時(shí)使用一些已經(jīng)被證明正確可行的解決方案。設(shè)計(jì)模式描述了它所要解決的問題,問題的上下文,和可重用的解決方案。通常對(duì)于所要解決的問題,我們會(huì)給它有一個(gè)名字,我們可以用這個(gè)名字和其他開發(fā)者交流該解決方案。
應(yīng)用框架通常會(huì)依賴于設(shè)計(jì)模式來幫助獲得一個(gè)靈活的通用的應(yīng)用程序設(shè)計(jì)。設(shè)計(jì)模式通過間接和抽象等手段,使得開發(fā)者可以很方便的把自己的類和組件添加到系統(tǒng)中去。
在應(yīng)用框架的幫助下,開發(fā)者可以及時(shí)的完成應(yīng)用程序的開發(fā),應(yīng)用程序可以按照用戶的需要進(jìn)行定制,而且開發(fā)者還可以享受到成熟框架帶來的健壯性和穩(wěn)定性。不過,這也是有代價(jià)的——你必須要學(xué)習(xí)和理解如何和應(yīng)用框架進(jìn)行交互,甚至你還要學(xué)習(xí)應(yīng)用框架的缺點(diǎn)。大部分的框架都具有很高層次的抽象,是很復(fù)雜的軟件產(chǎn)品。理解一個(gè)框架可能很困難,而對(duì)框架進(jìn)行調(diào)試就更是艱巨了。雖然框架提供了某些進(jìn)行定制的機(jī)制,但是這也可能給你帶來了某些限制或者某些技術(shù)上的特殊要求,就算你要執(zhí)行的功能只是稍微脫離該框架的范圍,這個(gè)缺點(diǎn)也可能會(huì)特別突出。
在你使用應(yīng)用框架之前,很重要的一點(diǎn)就是要明白這些事情:它的好處和不足、它的目標(biāo)應(yīng)用領(lǐng)域、它的組件和結(jié)構(gòu)、開發(fā)流程、基礎(chǔ)的設(shè)計(jì)模式和編程技術(shù)。
JHotDraw的描述
與JFC Swing不同,JHotDraw為一個(gè)基于GUI的編輯器提供了下面這些東西:整合在一個(gè)工具調(diào)色板中的各種工具、不同的視圖、用戶自定義的圖形元素、還有對(duì)圖像保存、載入和打印的支持。我們可以通過繼承或者組合某個(gè)組件的方式來對(duì)應(yīng)用框架進(jìn)行定制。
除了the main drawing window之外,JHotDraw對(duì)其他windows的支持甚少,比如它就不支持文本編輯器。不過你在有了一些JHotDraw的結(jié)構(gòu)的知識(shí)之后,應(yīng)該能很容易的擴(kuò)展這個(gè)框架來支持那些被忽略掉的功能。運(yùn)行例子程序的時(shí)候,你就會(huì)知道用JHotDraw開發(fā)的典型程序是什么樣子的了(譯者注:在JHotDraw的主頁上可以下載JHotDraw的源代碼,其中包括例子程序)。比如說,JavaDraw(譯者注:這是JHotDraw源程序中的一個(gè)例子程序)就是一個(gè)標(biāo)準(zhǔn)的繪圖程序,它能讓我們大約知道JHotDraw大概能能干什么。你可以在你解壓JHotDraw(譯者注:JHotDraw主頁www.jhotdraw.org)的所生成的目錄下通過輸入以下的命令啟動(dòng)JavaDraw:
java CH.ifa.draw.samples.javadraw.JavaDrawApp
另外,CH.ifa.draw.samples.pert.PertApplication也展示了一些JHotDraw定制化的可能性。
從軟件工程的角度看,JHotDraw也是很有趣的。JHotDraw一開始是由Kent Beck 和 Ward Cunningham用Smalltalk開發(fā)的,它是第一個(gè)公開聲言要以應(yīng)用框架的設(shè)計(jì)來進(jìn)行重用的開發(fā)項(xiàng)目。它在很早的時(shí)候就以設(shè)計(jì)模式的方式來編制文檔了,所以在設(shè)計(jì)模式社區(qū)頗有影響力。Erich Gamma和Thomas Eggenschwiler開發(fā)了JHotDraw的最初版本。
這篇文章談?wù)摿薐HotDraw一個(gè)新的版本——5.2(譯者注:在翻譯這篇文章的時(shí)候已經(jīng)有了6.1beta了),在這個(gè)版本里,原來的AWT組件被換成了相應(yīng)的JFC Swing組件。它也支持一些新的JFC Swing 特性,比如內(nèi)部窗體、滾動(dòng)條、工具條和彈出式菜單等。因此,JHotDraw作為一個(gè)專用的GUI框架,它還是基于的是Swing框架提供的通用的GUI設(shè)施的,不過在這些設(shè)施之上,JHotDraw又提供自己的一些特性和功能。
程序包組織
所有的JHotDraw類和接口都按照他們的功能放在不同的程序包中。CH.ifa.draw.framework里存放的是核心組件所需要的大部分接口,這些接口描述了核心組件的責(zé)任、功能和內(nèi)部操作。你可以在CH.ifa.draw.standard里找到這些接口的標(biāo)準(zhǔn)實(shí)現(xiàn)。在CH.ifa.draw.figures 和 CH.ifa.draw.contrib 里你能找到一些附加的功能。桌面應(yīng)用程序和applet程序的結(jié)構(gòu)被分別定義在CH.ifa.draw.application 和 CH.ifa.draw.applet中。
JHotDraw的結(jié)構(gòu)
如果我們更仔細(xì)的關(guān)注這些程序包,特別是那些核心的框架包,我們就能了解JHotDraw的結(jié)構(gòu),然后揭示出這些組件每一部分所擔(dān)當(dāng)?shù)慕巧恕?span xml:lang="en-us" lang="en-us">
任何使用JHotDraw的應(yīng)用程序都有一個(gè)用于畫圖的窗口。這個(gè)畫圖窗口——DrawWindow,是一個(gè)編輯器窗口,它是javax.swing.JFrame的子類。這個(gè)窗口有一個(gè)或更多個(gè)內(nèi)部窗體,每個(gè)窗體都與一個(gè)Drawing View相連。DrawingView是javax.swing.JPanel的子類,它是一個(gè)可以顯示圖像和接受用戶輸入的區(qū)域。圖形的改變會(huì)傳播到DrawingView,然后DrawingView負(fù)責(zé)更新這些圖像。圖形由figure組成,每個(gè)figure又能作為其他figure的容器。每個(gè)figure有一個(gè)handle,它用于定義訪問點(diǎn),決定該figure如何與其他figure交互(比如說,將這個(gè)figure與其他figure相連接)。在DrawingView里,你可以選中一個(gè)或者多個(gè)figure,然后處理它們。DrawWindows的本身有一個(gè)從工具調(diào)色板中得來的活動(dòng)的工具,這個(gè)工具可以對(duì)當(dāng)前DrawingView的圖形進(jìn)行操作。
JHotDraw的典型開發(fā)過程
下面的列表里的是使用JHotDraw開發(fā)應(yīng)用程序時(shí)經(jīng)常涉及到的任務(wù)。這些任務(wù)關(guān)注于如何將JHotDraw整合到你的應(yīng)用程序中,如何讓你用本文“一個(gè)簡單應(yīng)用程序的問題描述”章節(jié)所提到的對(duì)象模型一起工作。
1. 為你的應(yīng)用程序創(chuàng)建你自己的圖形元素和符號(hào)??赡苣憬?jīng)常都需要定義自己的圖形元素。很幸運(yùn)的,這已經(jīng)提前定義好了幾種圖形元素:AbstractFigure、CompositeFigure、AttributeFigure。你可以通過繼承這些類,然后重定義某些方法,比如draw(),來重定義它們的行為,從而達(dá)到在圖表中定制圖形表示的目的。通常情況下,這些圖形元素應(yīng)該和你在應(yīng)用程序中使用的對(duì)象保持一致,至少要有某種聯(lián)系。
2. 開發(fā)你自己的工具,用于創(chuàng)建圖形元素,并按照應(yīng)用的需要來對(duì)圖形元素進(jìn)行加工。JHotDraw再次帶來了便利,它已經(jīng)給我們準(zhǔn)備好了一些工具:比如說創(chuàng)建工具、聯(lián)接工具、選擇工具和文字工具。通過繼承這些類并重定義其中的某些方法,比如像mouseUP()、mouseDown(),你就可以定義你的程序的交互過程,執(zhí)行你的應(yīng)用程序所需要的任務(wù)——比如加工在你的應(yīng)用程序中所定義的對(duì)象。
3. 生成實(shí)際的GUI,并將其整合進(jìn)你的程序中去。毫不奇怪,JHotDraw已經(jīng)有了一個(gè)基本的應(yīng)用程序框架了:或者是一個(gè)基本的DrawApplication,或者是一個(gè)支持很多內(nèi)部frame的MDI_DrawApplication,再或者是一個(gè)DrawApplet。你可以通過重定義createMenus()、createFileMenu()和其他的辦法來定義你自己的菜單,通過重定義createTools()來插入新的工具。最后在運(yùn)行時(shí)實(shí)例化你的應(yīng)用程序然后調(diào)用open(),一個(gè)完整的GUI就產(chǎn)生了。
4. 使用javac編譯你的應(yīng)用程序。記住,把所有JHotDraw需要的的程序包在調(diào)用java或者javac命令之前加入到classpath中去。
這些任務(wù)里有些涉及到某些設(shè)計(jì)模式的應(yīng)用,本文將在遲些時(shí)候進(jìn)行具體的討論。
一個(gè)簡單應(yīng)用程序的問題描述
在使用一個(gè)框架之前,知道它的目標(biāo)應(yīng)用領(lǐng)域和它是如何解決在這個(gè)領(lǐng)域出現(xiàn)的問題是很重要的(如果有一個(gè)目標(biāo)應(yīng)用領(lǐng)域存在的話)。
在本文里,我們討論一個(gè)名叫JModeller的簡單的類圖編輯器的開發(fā)過程,這可以看作是JHotDraw的簡單應(yīng)用。JModeller幫助設(shè)計(jì)類圖,將軟件架構(gòu)制作成文檔。它支持類之間的聯(lián)系、聚合、以來和繼承關(guān)系。除了這些基本的設(shè)計(jì)結(jié)構(gòu)和圖形的保存、載入和打印功能之外,這個(gè)編輯器就沒有別的更高級(jí)的特性了。
在這個(gè)類圖編輯器里的為了管理類所涉及對(duì)象模型是相當(dāng)簡單的。在這個(gè)模型里的主要的類是JModellerClass,它代表了一個(gè)類,有類名、屬性和方法組成。如果類之間的關(guān)聯(lián)、依賴和繼承關(guān)系變的復(fù)雜,你可以在模型中添加一個(gè)專用的類來跟蹤這些關(guān)系的屬性和行為。目前為止這并不需要,所以由類本身來存儲(chǔ)類間關(guān)系的信息。而且,有專門的圖形元素用于繪制類、關(guān)聯(lián)線、依賴線或者是繼承線。用于開發(fā)一個(gè)類編輯器的對(duì)象模型和圖形編輯器使用的用于類圖設(shè)計(jì)的對(duì)象模型是不同的,讀者不應(yīng)混淆。圖形編輯器里使用的具體的對(duì)象模型只是存儲(chǔ)一些用于類圖的信息和一些圖形信息。很明顯,用JHotDraw構(gòu)建的應(yīng)用程序可以很好的滿足上面所敘述的要求。
使用JHotDraw的設(shè)計(jì)模式
現(xiàn)在我將向讀者展示如何利用JHotDraw里的設(shè)計(jì)模式類開發(fā)類圖編輯器。
MVC框架
首先,請(qǐng)讀者清楚的一點(diǎn)就是JHotDraw是基于MVC模型的,這個(gè)模型將應(yīng)用邏輯和用戶界面分離開。View通常用于在用戶界面上顯示信息;而controller用于處理用戶的交互然后將其映射到應(yīng)用功能上。Model部分作為view和controller部分的基礎(chǔ),由應(yīng)用邏輯和數(shù)據(jù)組成。在數(shù)據(jù)上的任何變化都會(huì)通知到View上的。
復(fù)合設(shè)計(jì)模式
正如本文之前提到的,用于在一個(gè)類圖里存儲(chǔ)信息的對(duì)象模型是很簡單的。你只需要一個(gè)簡單的JModellerClass,代碼可以在JModellerClass.java里找到。在類圖里表示類的圖形元素更有趣些。它們有許多基本圖形元素不能滿足的要求。所以我們創(chuàng)建了一個(gè)新的圖形元素:GraphicalCompositeFigure;這個(gè)圖形元素整合了好幾種已經(jīng)存在的圖形元素的優(yōu)點(diǎn),特別是TextFigure,它被用于顯示類的名字、屬性和方法。JHotDraw已經(jīng)提供了一種圖形元素叫做CompositeFigure,它可以將很多種圖形元素整合在一起。
就如這個(gè)圖形元素的名字一樣,相應(yīng)的設(shè)計(jì)模式就叫做復(fù)合設(shè)計(jì)模式。它和其他的一些設(shè)計(jì)模式一樣,都在Erich Gamma的那本《Design Patterns》書里有所敘述。在復(fù)合設(shè)計(jì)模式背后的思想就是在一個(gè)容器里有很多個(gè)屬于相同基本類型的組件,但這個(gè)容器本身也可以被當(dāng)作一個(gè)單獨(dú)的組件。容器方法調(diào)用里的所有行為被容器委托到它里面的組件上。通常情況下,客戶端的組件不會(huì)意識(shí)到是在和元素的組合體在打交道,它將其當(dāng)作一個(gè)單獨(dú)的組件。這種封裝技術(shù)使得我們可以用一個(gè)組件的繼承層次結(jié)構(gòu)來創(chuàng)建一個(gè)像CompositeFigure這樣的復(fù)合體,在這個(gè)結(jié)構(gòu)中的所有組件就像是一個(gè)單元整體的樣子來與別的模塊交互。有趣的是,StandardDrawing是CompositeFigure的子類,這意味著一副圖像可以包含其他的圖形元素,而它本身也是一個(gè)圖形元素。
對(duì)于復(fù)合體如何把一個(gè)行為分派給它里面的組件,可以找到一個(gè)很好的例子,就是CH.ifa.draw.standard.CompositeFigure的draw()方法
/**
* Draws all the contained figures
* @see Figure#draw
*/
public void draw(Graphics g) {
FigureEnumeration k = figures();
while (k.hasMoreElements())
k.nextFigure().draw(g);
}
通常情況下CompositeFigure并沒有自己的圖形表示,它只是簡單的顯示它所復(fù)合的其他Figure。不過一個(gè)類的圖形表示必須要能考慮到它的全部圖形表示,并能讓它的的圖形容器也顯示出來。GraphicalCompositeFigure是一個(gè)CompositeFigure,它也同樣的把它自己的圖形表示功能委托給一個(gè)專用的圖形元素上。如果一個(gè)AttributeFigure(或者是它的子類)被用作一個(gè)圖形元素,那GraphicalCompositeFigure就也可以用來保存想字體信息和顏色了。
public class GraphicalCompositeFigure extends CompositeFigure {
....
/**
* Return the display area. This method is delegated to the encapsulated presentation figure.
*/
public Rectangle displayBox() {
return getPresentationFigure().displayBox();
}
/**
* Draw the figure. This method is delegated to the encapsulated presentation figure.
*/
public void draw(Graphics g) {
getPresentationFigure().draw(g);
super.draw(g);
}
....
}
現(xiàn)在你所需要做的就是創(chuàng)建一個(gè)ClassFigure類,它從GraphicalCompositeFigure里繼承了所有需要的行為。ClassFigure類里包含一個(gè)TextFigure用于存放類名,還有其他的用于存放屬性和方法的不同圖形元素。一個(gè)在構(gòu)造器里的CH.ifa.draw.figures.RectangleFigure被用于draw a box around the whole container figure.
public class ClassFigure extends GraphicalCompositeFigure {
private JModellerClass myClass;
private GraphicalCompositeFigure myAttributesFigure;
private GraphicalCompositeFigure myMethodsFigure;
...
public ClassFigure() {
this(new RectangleFigure());
}
public ClassFigure(Figure newPresentationFigure) {
super(newPresentationFigure);
}
/**
* Hook method called to initialize a ClassFigure.
* It is called from the superclass' constructor and the clone() method.
*/
protected void initialize() {
// start with an empty Composite
removeAll();
// create a new Model object associated with this View figure
setModellerClass(new JModellerClass());
// create a TextFigure responsible for the class name
setClassNameFigure(new TextFigure() {
public void setText(String newText) {
super.setText(newText);
getModellerClass().setName(newText);
update();
}
});
// add the TextFigure to a Composite
GraphicalCompositeFigure nameFigure = new GraphicalCompositeFigure(new SeparatorFigure());
nameFigure.add(getClassNameFigure());
...
add(nameFigure);
// create a figure responsible for maintaining attributes
setAttributesFigure(new GraphicalCompositeFigure(new SeparatorFigure()));
...
add(getAttributesFigure());
// create a figure responsible for maintaining methods
setMethodsFigure(new GraphicalCompositeFigure(new SeparatorFigure()));
...
add(getMethodsFigure());
setAttribute(Figure.POPUP_MENU, createPopupMenu());
super.initialize();
}
...
}
策略設(shè)計(jì)模式
用于表示ClassFigure類的圖形元素只負(fù)責(zé)本元素的繪制;這個(gè)圖形元素并不知道怎么放置ClassFigure,也不知道ClassFigure中的該怎么安排各個(gè)子組件之間的圖形顯示。實(shí)際上,圖形表示和布局管理的算法是相互獨(dú)立的。結(jié)果,布局算法被從ClassFigure中分離出來,并被封裝成一個(gè)外部的類,不過它對(duì)這個(gè)ClassFigure有廣泛的訪問權(quán)限,并能對(duì)其進(jìn)行控制。如果我們要放置一個(gè)ClassFigure,這項(xiàng)任務(wù)會(huì)被委托給CH.ifa.draw.contrib.FigureLayoutStrategy類來完成,這個(gè)類有遍歷組件所有子組件并且放置他們的邏輯步驟?!?span xml:lang="en-us" lang="en-us">
狀態(tài)設(shè)計(jì)模式
在JHotDraw里,工具調(diào)色板里有很多工具,你可以用他們來選擇、加工或者創(chuàng)建一個(gè)圖形元素。在某些時(shí)候,你可能還需要定義自己的工具來完成別的功能。正如我們所看到的,一個(gè)ClassFigure包含很多個(gè)TextFigure用于保存類名、屬性和方法的信息。不過不幸的是,CH.ifa.draw.standard.SelectionTool只能激活被選中的容器,它并不包含任何TextFigures。如果你想通過雙擊某個(gè)ClassFigure來編輯它所包含的TextFigures,那可以使用CH.ifa.draw.contrib.CustomSelectionTool。這個(gè)類能夠?qū)崿F(xiàn)你的目的,而且還會(huì)幫你處理圖形元素的彈出窗口。我們?cè)谶@個(gè)編輯器里使用的選擇工具就是源于這個(gè)類,然后重定義了它的handleMouseDoubleClick 和 handleMouseClick方法。如果發(fā)生了雙擊的時(shí)間,這個(gè)工具會(huì)激活一個(gè)CH.ifa.draw.figures.TextTool用于負(fù)責(zé)對(duì)文本的編輯。
public class DelegationSelectionTool extends CustomSelectionTool {
private TextTool myTextTool;
public DelegationSelectionTool(DrawingView view) {
super(view);
setTextTool(new TextTool(view, new TextFigure()));
}
protected void handleMouseDoubleClick(MouseEvent e, int x, int y) {
Figure figure = drawing().findFigureInside(e.getX(), e.getY());
if ((figure != null) && (figure instanceof TextFigure)) {
getTextTool().activate();
getTextTool().mouseDown(e, x, y);
}
}
protected void handleMouseClick(MouseEvent e, int x, int y) {
deactivate();
}
public void deactivate() {
super.deactivate();
if (getTextTool().isActivated()) {
getTextTool().deactivate();
}
}
...
}
在畫板上進(jìn)行操作的選擇工具是屬于drawing view的。因此,drawing view總是有一個(gè)處于活動(dòng)狀態(tài)的工具。當(dāng)改變工具的時(shí)候,它的行為和與用戶交互的方式也改變了。換句話說,工具就是在drawing view的上下文環(huán)境中的一種狀態(tài)。在理想狀態(tài)下,drawing view應(yīng)該和實(shí)際的工具是相互獨(dú)立的,所以工具之間應(yīng)該是可以相互替換,我們也不需要用復(fù)雜的if/switch語句來區(qū)分這些工具。在一個(gè)狀態(tài)設(shè)計(jì)模式里,你使用代表狀態(tài)的對(duì)象來講狀態(tài)和狀態(tài)的上下文區(qū)分開,而這個(gè)代表狀態(tài)的對(duì)象定義了一個(gè)上下文可以使用的接口。因此,在一個(gè)以drawing view為上下文的環(huán)境里,引入一個(gè)DelegationSelectionTool工具,只是引入了一個(gè)另一個(gè)新的狀態(tài)而已。Drawing view還是像往常一樣接受用戶的輸入,不過將這些輸入委托給工具來處理。工具知道該如何來處理用戶的輸入,它會(huì)相應(yīng)的執(zhí)行某些任務(wù)。
工具決定了StandardDrawingView操作的狀態(tài)
狀態(tài)模式看起來有點(diǎn)像是策略模式,它們都將行為委托給某個(gè)特定的對(duì)象,不過它們的目的是不一樣的。策略模式將對(duì)象和算法之間解耦,使得算法可以被重用。而狀態(tài)模式將內(nèi)在的行為分離和提取出來,使得其可以容易的擴(kuò)展和相互替換。
模板設(shè)計(jì)模式
類圖顯示類,而類之間是有關(guān)系的。AssociationLineConnection元素代表了兩個(gè)類之間的線性聯(lián)系,它可以變成直接的聯(lián)系或者是聚合關(guān)系。InheritanceLineConnection用一條從子類指向父類的帶箭頭的直線表示一種繼承關(guān)系。CH.ifa.draw.figures.LineConnection類通過提供connectEnd() and disconnectEnd()來實(shí)現(xiàn)模板方法。
在LineConnection中的模板方法設(shè)計(jì)模式和鉤子方法
模板方法又是一種設(shè)計(jì)模式,它定義了一個(gè)總是要執(zhí)行的命令序列。在這個(gè)命令序列中,另外一些方法總是在某個(gè)確定的時(shí)候被調(diào)用。于是子類可以在不改變總體的行為的情況下添加和具體,應(yīng)用相關(guān)的指令了。因此AssociationLineConnection 和InheritanceLineConnection只需要對(duì)這些需要的鉤子函數(shù)進(jìn)行重定義了——handleConnect() 和 handleDisconnect(),從而可以建立起兩個(gè)類之間的關(guān)系。下面的代碼演示了這點(diǎn):
public class InheritanceLineConnection extends LineConnection {
...
/**
* Hook method to plug in application behavior into
* a template method. This method is called when a
* connection between two objects has been established.
*/
protected void handleConnect(Figure start, Figure end) {
super.handleConnect(start, end);
JModellerClass startClass = ((ClassFigure)start).getModellerClass();
JModellerClass endClass = ((ClassFigure)end).getModellerClass();
startClass.addSuperclass(endClass);
}
...
}
在建立連接之前,模板方法必須要執(zhí)行一系列的步驟。首先調(diào)用canConnect測(cè)試兩個(gè)圖形元素是否可以連接,然后設(shè)置開始和結(jié)束端的圖形元素,最后調(diào)用鉤子方法handleConnect
模板方法調(diào)用鉤子方法handleConnect()
另一個(gè)模板方法的例子可以在CH.ifa.draw.standard.AttributeFigure里找到,它其中的draw()方法定義了繪圖的程序,在這個(gè)方法當(dāng)中會(huì)調(diào)用drawBackground()和drawFrame()方法,而這兩個(gè)方法會(huì)被諸如CH.ifa.draw.figures.RectangleFigure之類的類重定義。
工廠方法
工廠方法是《Design Patterns》一書里記述的另一種設(shè)計(jì)模式。你可以將工廠方法想像成一個(gè)在構(gòu)造過程中的特殊的鉤子方法。這個(gè)設(shè)計(jì)模式讓你可以創(chuàng)建定制化的組件。這個(gè)方法在JHotDraw里被廣泛的使用,特別是在創(chuàng)建如菜單和工具等用戶界面組件的時(shí)候。你可以在CH.ifa.draw.application.DrawApplication里找到大量的工廠方法,它們有諸如createTools()或者是createMenus()之類的方法,或者,更進(jìn)一步的,叫做createFileMenu(), createEditMenu()等等名字。根據(jù)你想定制的粒度,你可以將合適的創(chuàng)建方法進(jìn)行改變。為了插入用于創(chuàng)建類或者用于類之間的關(guān)聯(lián)和繼承關(guān)系的工具,你可以在你的應(yīng)用程序里重定義createToo方法。
public class JModellerApplication extends MDI_DrawApplication {
...
public JModellerApplication() {
super("JModeller - Class Diagram Editor");
}
/**
* Create the tools for the toolbar. The tools are
* a selection tool, a tool to create a new class and
* two tools to create association and inheritance
* relationships between classes.
*
* @param palette toolbar to which the tools should be added
*/
protected void createTools(JToolBar palette) {
super.createTools(palette);
Tool tool = new ConnectedTextTool(view(), new TextFigure());
palette.add(createToolButton(IMAGES+"ATEXT", "Label", tool));
tool = new CreationTool(view(), new ClassFigure());
palette.add(createToolButton(DIAGRAM_IMAGES+"CLASS", "New Class", tool));
tool = new ConnectionTool(view(), new AssociationLineConnection());
palette.add(createToolButton(IMAGES+"LINE", "Association Tool", tool));
tool = new ConnectionTool(view(), new DependencyLineConnection());
palette.add(createToolButton(DIAGRAM_IMAGES+"DEPENDENCY", "Dependency Tool", tool));
tool = new ConnectionTool(view(), new InheritanceLineConnection());
palette.add(createToolButton(DIAGRAM_IMAGES+"INHERITANCE", "Inheritance Tool", tool));
}
...
}
你必須重定義createSelectionTool()方法來創(chuàng)建你自己的createSelectionTool:
protected Tool createSelectionTool() {
return new DelegationSelectionTool(view());
}
原型設(shè)計(jì)模式
你可能已經(jīng)留意到了在createTool方法里,每種工具都是以工具所要產(chǎn)生的一個(gè)圖形元素的實(shí)例來初始化的。在JHotDraw里,每一個(gè)創(chuàng)建工具都是利用一個(gè)原始的圖形元素的實(shí)例來創(chuàng)建重復(fù)的實(shí)例的?!T?span xml:lang="en-us" lang="en-us">CH.ifa.draw.standard.AbstractFigure里定義了基本的clone()機(jī)制,在這個(gè)方法里,運(yùn)用Java的串行化機(jī)制對(duì)對(duì)象進(jìn)行了復(fù)制。
串行化是一種很簡單的方法,它將整個(gè)對(duì)象結(jié)構(gòu)先寫出,然后再讀入。這會(huì)創(chuàng)建原始圖形元素的深復(fù)制副本,其中包括了所有引用對(duì)象的副本。因此,原始圖形元素和它的副本并不共享任何對(duì)象引用。比如說,ClassFigure并不會(huì)串行化任何想聯(lián)系的上下文菜單,這些菜單必須在反串行化的時(shí)候被初始化。
private void readObject(ObjectInputStream s)
// call superclass' private readObject() indirectly
s.defaultReadObject();
// Create popup menu after it has been read
setAttribute(Figure.POPUP_MENU, createPopupMenu());
}
而且,讀取和寫入圖形及其所包含的圖形元素也會(huì)簡單的調(diào)用串行化機(jī)制。JHotDraw……。每個(gè)圖形元素都實(shí)現(xiàn)了CH.ifa.draw.util.Storable接口,它們各自提供了用于如何將他們自己寫入磁盤和讀取出來的實(shí)現(xiàn)。被寫入的信息包括類名和所有屬性的值,這些值以String的形式被寫入。很明顯,屬性值被寫入的順序是很重要的。利用類名,JHotDraw使用Java的反射機(jī)制產(chǎn)生一個(gè)新的圖形實(shí)例。在這種情況下,空參數(shù)列表的默認(rèn)構(gòu)造器會(huì)被調(diào)用。
Mian方法
……JModellerApplication……
/**
* Start the application by creating an instance and open
* the editor window.
*/
public static void main(String[] args) {
JModellerApplication window = new JModellerApplication();
window.open();
}
使用JModeller
JModeller的使用是相當(dāng)簡單的。這其中只有很少的障礙需要進(jìn)一步的簡短解釋。首先,每一個(gè)ClassFigure都有一個(gè)上下文菜單,它讓你可以為類加入屬性和方法。通過雙擊類的名字、屬性或方法,你可以編輯上面的文本??盏膶傩曰蚍椒韺⒛莻€(gè)屬性或者方法刪除,空的類名則代表將整個(gè)類刪除。AssociationLineConnection類同樣也有一個(gè)上下文菜單,它讓你可以把關(guān)聯(lián)改為聚合,可以在bidirectional and unidirectional聯(lián)系之間轉(zhuǎn)換。你可以在ConnectedTextTool工具的幫助下在類的關(guān)系上加上注釋。通過使用合適的連接工具來點(diǎn)選連接線,你可以在任何兩個(gè)類的關(guān)系上增加一個(gè)處理點(diǎn)。最后,在你編譯或者運(yùn)行JModeller的時(shí)候,你必須保證jhotdraw.jar在你的classpath里。比如說,在Windows下你可以通過下面這樣的命令行命令來編譯JModeller:
javac -classpath "%CLASSPATH%;/jhotdraw.jar;." *.java
通過這樣的命令你可以啟動(dòng)程序:
java -classpath "%CLASSPATH%;/jhotdraw.jar;." JModellerApplication
里必須是你解壓JHotDraw后產(chǎn)生的目錄。
結(jié)論
要想正確的使用一個(gè)框架,你必須先理解它。設(shè)計(jì)模式是一種可以達(dá)到高度可復(fù)用的軟件架構(gòu)的方法,它也非常適合用于將架構(gòu)寫成文檔以方便別的開發(fā)者理解。JHotDraw就是一個(gè)框架的好例子,它由一些基礎(chǔ)的設(shè)計(jì)模式組成,比如……。了解了在這些模式之后的基本概念和這些模式在JHotDraw里的應(yīng)用會(huì)幫助你決定如何定制和調(diào)整這個(gè)框架以滿足你的應(yīng)用程序的需求。雖然學(xué)習(xí)一個(gè)框架需要額外的努力,在一開始甚至?xí)下愕倪M(jìn)度,不過像JHotDraw這樣一個(gè)框架通常都在可以縮短開發(fā)時(shí)間的同時(shí)提高你的軟件質(zhì)量。當(dāng)然,JModeller只是JHotDraw強(qiáng)大能力的一個(gè)小小的例子,希望這篇文章會(huì)是你親自動(dòng)手實(shí)驗(yàn)體會(huì)這一點(diǎn)的開始。
聯(lián)系客服