前言
之所以說是再戰,是因爲在沒有學習設計模式之前已經基于MVC體系結構做過一些項目,主要是小項目,當初理解MVC有一些困難。現在已經把Gof所說的相對簡單但是最常見的這些設計模式:
Abstract Factory
Adapter
Composite
Decorator
Factory Method
Observer
Strategy
Template Method
都詳細過了一遍了,而我們都知道MVC是一種比較特殊的模式,MVC並不屬于GOF的23個設計模式之列,但是Smalltalk的MVC框架結構在著名的GOF書中作爲一個重要的例子被提出來,並給予了很高的評價。一般的來講,我們認爲GOF的23個模式是一些中級的模式,在它下面還可以抽象出一些更爲一般的低層的模式,在其上也可以通過組合來得到一些高級的模式,即架構模式。MVC就可以看作是一些模式進行組合之後的結果(實際上,MVC的出現要早于設計模式的提出,因而只是對它在設計模式的基礎上進行在分析)。所以說現在有必要站在設計模式的思想上來看MVC模式。<<Java與模式>>在MVC模式與用戶輸入數據檢查這個專題裏大量地談到MVC模式跟設計模式的關系,本文按這個專題的思路以筆記的形式展開。
MVC模式可以分解爲以下設計模式
在GOF書的Introduction中,有一小節是“Design Patterns in Smalltalk MVC”即介紹在MVC模式裏用到的設計模式。它大概向我們傳達了這樣的信息:合成模式+策略模式+觀察者模式約等于MVC模式(當然MVC模式要多一些東西)。也就是說它在大部分情況下是下面幾個模式:
1觀察者模式
類圖結構在Gof裏的表示如下:
2 合成模式
類圖結構在Gof裏的表示如下:
3策略模式
除了這三種主要的設計模式,Gof最後也提到了MVC還使用了其它的設計模式,比如說工廠方法模式用來說明對一個view的默認的控制器,還有裝飾模式用來爲view增加一個滾動條等。但是在MVC模式裏起主要作用的還是前面列出來的三個設計模式。
模式的分類
書中提到由于[GOF95]是論述軟件模式的著作的第一本,也是OO設計理論著作中最流行的一本,因此有些人常常使用設計模式(Design Pattern)一詞來指所有直接處理軟件架構,設計,程序實現的任何種類的模式。另外一種說法是強調要劃分三種不同層次的模式:架構模式(Architectual Pattern),設計模式(Design Pattern),成例(Idiom)。成例有時稱爲代碼模式(Codeing Pattern)。
架構模式(Architectual Pattern)
面向對象軟件大師Martin Fowler有本書叫做<< Patterns of Enterprise Application Architecture>>中文名叫<<企業應用架構模式>>。這本書就是關于如何將企業應用分層,以及如何組織各層工作的。例如這本書在第二部分深入講解模式時,在談到了Web表示層的時候就分析了MVC模式,從這些我們可以知道架構模式提供一些事先定義好的子系統,指定他們的責任,並給出把它們組織在一起的法則和指南,一個架構模式常常可以分解成很多個設計模式的聯合使用。
設計模式
設計模式描述在軟件設計過程中普遍存在相互通信的組件中重複出現的結構,這種結構解決在一定的背景中的具有一般性的設計問題。
代碼模式或成例(Coding Pattern或Idiom)
代碼模式(或成例)是較低層次的模式,並與編程語言密切相關。代碼模式描述怎樣利用一個特定的編程語言的特點來實現一個組件的某些特定的方面或關系。
從上面的敘述中其實我們不難從一個軟件系統中找到他們,因爲三種不同的模式存在于它們自的抽象層次和具體層次上,所以少了其中任何一種模式,或者有一種設計的不好,都會給整個軟件系統帶來害處,如可維護性低等缺點。因爲架構模式是一個系統的高層次策略,涉及到大尺度的組件以及整體性質和力學,它設計的好壞可以影響到總體布局和框架性結構。設計模式是中等尺度的結構策略。這些中等尺度的結構實現了一些大尺度組件的行爲和它們之間的關系,所以設計模式的好壞不會影響到系統的總休布局和總體框架,但是它會影響到系統的擴展性,維護性等方面。代碼模式是特定的範例和與特定編程語言有關的編程技巧。它不會影響到一個部件或子系統的中等尺度的結構,更不會影響到系統的總體布局和大尺度框架,但是會影響到一個中等尺度組件的內部,外部的結構或行爲的底層細節。
談MVC架構模式
MVC中的三個角色
Model(模型端)
Mod封裝的是數據源和所有基于對這些數據的操作。在一個組件中,Model往往表示組件的狀態和操作這些狀態的方法,往往是一系列的公開方法。通過這些公開方法,便可以取得模型端的所有功能。
在這些公開方法中,有些是取值方法,讓系統其他部分可以得到模型端的內部狀態參數,其他的改值方法則允許外部修改模型端的內部狀態。模型端還必須有方法登記視圖,以便在模型端的內部狀態發生變化時,可以通知視圖端。我們可以自己定義一個Subject接口來提供登記和通知視圖所需的接口或者繼承Java.util.Observable類,讓父類完成這件事。
多個View(視圖端)
View封裝的是對數據源Model的一種顯示。一個模型可以由多個視圖,並且可以在需要的時候動態地登記上所需的視圖。而一個視圖理論上也可以同不同的模型關聯起來。
在前言裏提到了,MVC模式用到了合成模式,這是因爲在視圖端裏,視圖可以嵌套,比如說在一個JFrame組件裏面,可以有菜單組件,很多按鈕組件等。
多個Controller(控制器端)
封裝的是外界作用于模型的操作。通常,這些操作會轉發到模型上,並調用模型中相應的一個或者多個方法(這個方法就是前面在介紹模型的時候說的改值方法)。一般Controller在Model和View之間起到了溝通的作用,處理用戶在View上的輸入,並轉發給Model來更改其狀態值。這樣Model和View兩者之間可以做到松散耦合,甚至可以彼此不知道對方,而由Controller連接起這兩個部分。也在前言裏提到,MVC用到了策略模式,這是因爲View用一個特定的Controller的實例來實現一個特定的響應策略,更換不同的Controller,可以改變View對用戶輸入的響應。
MVC模式用在基于C/S的應用
盡管MVC設計模式通常是用來設計整個用戶界面(GUI)的,JFC的設計者們卻獨創性的把這種設計模式用來設計Swing中的單個的組件(Component),例如表格Jtable等。下面的討論也就是對于Swing單個組件所體現出來的這種MVC模式來說的。
嚴格地講,Swing中的MVC實際上是MVC的一個變體:M-VC。 拿JTable來說,MVC 就是 TableModel JTable TableUI. Swing中只顯示的定義了Model接口(TableModel),而在一個UI對象中集成了視圖和控制器的部分機制(JTable)。View和Control比較松散的交叉組合在一起,而更多的控制邏輯是在事件監聽者部分引入的爲了更好地展現Swing是一個設計優秀的Java包,充滿了大師的智慧,讓我們更加深入的進行分析,我將采用最常見的組件button來說明。
注:以下例子是摘自網上一篇名爲“通過Java Swing看透MVC設計模式”的佚名文章,感覺對從Swing庫的設計來看MVC模式的應用有相當大的幫助,故插到此處同大家分享,黑色字迹部分爲自己的說明。
(轉載開始)
Model的設計
一個按鈕的model所應該具備的行爲由一個接口ButtonModel來完成。一個按鈕model實例封裝了其內部的狀態,並且定義了按鈕的行爲。它的所有方法可以分爲四類:
1、查詢內部狀態
2、操作內部狀態
3、添加和刪除事件監聽器
4、發生事件
程序員通常並不會直接和model以及view/controller打交道,他們通常隱藏于那些繼承自java.awt.Component的組件裏面了,這些組件就像膠水一樣把MVC三者合三爲一。也正是由于這些繼承的組件對象,一個程序員可以很方便的混合使用Swing組件和AWT組件,然後,我們知道,Swing組件有很多都是直接繼承自相應的AWT組件,它能提供比AWT組件更加方便易用的功能,所以通常情況下,我們沒有必要混合使用兩者。
一個實例
現在我們已經明白了Java類與MVC各個部分的對應關系,我們可以更加深入一點去分析問題了。下面將要講述一個小型的使用MVC模式開發的例子。因爲JFC十分的複雜,我只能把我的例子局限于一個用戶界面組件裏面(如果你猜是一個按鈕的例子,那麽你對了!)
讓我們來看看這個例子的所有部分吧。
Button組件
最顯而易見的開始的地方就是代表了按鈕組件本身的代碼,因爲這個類是大部分程序員會接觸的。
就像我前面提到的,按鈕用戶界面組件類實際上就是model和view/controller的之間的黏合劑。每個按鈕組件都和一個model以及一個controller關聯,model定義了按鈕的行爲,而view/controller定義了按鈕的表現。而應用程序可以在任何事件改變這些關聯。讓我們看看得以實現此功能的代碼。
注:以下代碼是我從JDK1.4源代碼AbstractButton類中提取出來的,因爲查看Jbutton源代碼(再次提供JDK1.4在線源代碼地址)我們可以發現,它是繼承自AbstractButton類的,所以下面的代碼是來自AbstractButton的,跟原文有點不一樣:
1. /**
2.
3. * Sets the model that this button represents.
4.
5. * @param m the new <code>ButtonModel</code>
6.
7. * @see #getModel
8.
9. * @beaninfo
10.
11. * bound: true
12.
13. * description: Model that the Button uses.
14.
15. */
16.
17. public void setModel(ButtonModel newModel) {
18.
19.
20.
21. ButtonModel oldModel = getModel();
22.
23.
24.
25. if (oldModel != null) {
26.
27. oldModel.removeChangeListener(changeListener);
28.
29. oldModel.removeActionListener(actionListener);
30.
31. changeListener = null;
32.
33. actionListener = null;
34.
35. }
36.
37.
38.
39. model = newModel;
40.
41.
42.
43. if (newModel != null) {
44.
45. changeListener = createChangeListener();
46.
47. actionListener = createActionListener();
48.
49. itemListener = createItemListener();
50.
51. newModel.addChangeListener(changeListener);
52.
53. newModel.addActionListener(actionListener);
54.
55. newModel.addItemListener(itemListener);
56.
57. mnemonic = newModel.getMnemonic();
58.
59. } else {
60.
61. mnemonic = '\0';
62.
63. }
64.
65.
66.
67. updateDisplayedMnemonicIndex(getText(), mnemonic);
68.
69.
70.
71. firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
72.
73. if (newModel != oldModel) {
74.
75. revalidate();
76.
77. repaint();
78.
79. }
80.
81. }
注:你可以多花一些時間按我上面提供的鏈接地址來仔細閱讀一下Button類及其它相關類的源代碼。
ButtonModel類
ButtonModel維護著三種類型的狀態信息:是否被按下(pressed),是否“武裝上了”(armed),是否被選擇(selected)。它們都是boolean類型的值。
一個按鈕被按下(pressed)是指當鼠標在按鈕上面的時候,按下鼠標但是還沒有松開鼠標按鈕的狀態,即使用戶此時把鼠標拖拽到按鈕的外面也沒有改變這種狀態。
一個按鈕是否“武裝了”(armed)是指按鈕被按下,並且鼠標還在按鈕的上面。
一些按鈕還可能被選擇(selected),這種狀態通過重複的點擊按鈕取得true或者false的值。
注:查看ButtonModel源代碼我們可以看到它是一個接口,定義了一組方法:
public interface ButtonModel extends ItemSelectable {
boolean isArmed();
boolean isPressed();
…
boolean isRollover();
void addActionListener(ActionListener l);
void removeActionListener(ActionListener l);
void addItemListener(ItemListener l);
…
void removeChangeListener(ChangeListener l);
其實看到這裏,如果你再回頭看下這個例子剛開始時“Model的設計”以及前面的內容,就會明白什麽是“Mod封裝的是數據源和所有基于對這些數據的操作。在一個組件中,Model往往表示組件的狀態和操作這些狀態的方法,往往是一系列的公開方法。通過這些公開方法,便可以取得模型端的所有功能”
我們再來看看AbstractButton的源代碼:
1. public abstract classimplements ItemSelectable, SwingConstants { AbstractButton extends JComponent
2. protected ButtonModel model= null;
3. protected ChangeListener changeListener = null;
4. protected ActionListener actionListener = null;
5. protected ItemListener itemListener = null;
6. protected transient ChangeEvent changeEvent;
7. public boolean isSelected() {
8. return model.isSelected();
9. }
10.public void doClick() {
11.doClick(68);
12. }
13.public void doClick(int pressTime) {
14. Dimension size = getSize();
15.model.setArmed(true);
16. model.setPressed(true);
17. paintImmediately(new Rectangle(0,0, size.width, size.height));
18.try {
19.Thread.currentThread().sleep(pressTime);
20. } catch(InterruptedException ie) {
21.}
22.model.setPressed(false);
23.model.setArmed(false);
24.}
25.//其它代碼
26.}
上面的代碼setArmed等方法是由DefaultButtonModel定義的,它是ButtonModel接口的一個默認實現。我們如果繼承了DefaultButtonModel,這樣可以覆蓋缺省的狀態定義,實現有個性的按鈕。ButtonUI類
按鈕的view/controller是負責構建表示層的。缺省情況下它僅僅是用背景色畫一個矩形而已,他們的子類繼承了他們並且覆蓋了繪制的方法,使得按鈕可以有許多不同的表現,例如MOTIF,Windows 95,Java樣式等等。
注:在javax.swing.plaf這個包裏我們可以找到ButtonUI,這是一個抽象類,類內部是空的,而在javax.swing.plaf.basic這個包裏我們可以找到BasicButtonUI,它繼承自ButtonUI,其實我們觀察下包結構會發現有javax.swing.plaf.metal,javax.swing.plaf.multi等,這幾個就是來控制Button在不同系統間的不同的顯示效果。:
public void update(Button button, Graphics graphics)
{
}
public void paint(Button button, Graphics graphics)
{
Dimension dimension = button.getSize();
Color color = button.getBackground();
graphics.setColor(color);
graphics.fillRect(0, 0, dimension.width, dimension.height);
}
ButtonUI類並不自己處理AWT事件,他們會使用一個定制的事件監聽器把低級的AWT事件翻譯爲高級的Button模型期望的語義事件。下面就是安裝/卸載事件監聽器的代碼。
private static ButtonUIListener buttonuilistener = null;
public void uninstallUI(Button button)
{
button.removeMouseListener(buttonuilistener);
button.removeMouseMotionListener(buttonuilistener);
button.removeChangeListener(buttonuilistener);
}
View/Controller實際上就是一些方法。他們不維護任何自己的狀態信息。因此,許多按鈕的實例可以共享一個ButtonUI實例。ButtonUI是通過在方便的參數列表裏面加上按鈕的引用來區分各個不同的按鈕。
ButtonUIListener類
ButtonUIListener類可以幫助Button類去轉變鼠標或者鍵盤的輸入爲對按鈕模型的操作。這個監聽器類實現了:MouseListener,MouseMotionListener,ChangeListener接口,並且處理一下事件:
public void mouseDragged(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
if (buttonmodel.isPressed())
{
if (button.getUI().contains(button, mouseevent.getPoint()))
{
buttonmodel.setArmed(true);
}
else
{
buttonmodel.setArmed(false);
}
}
}
public void mousePressed(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
buttonmodel.setPressed(true);
buttonmodel.setArmed(true);
}
public void mouseReleased(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
buttonmodel.setPressed(false);
buttonmodel.setArmed(false);
}
public void stateChanged(ChangeEvent changeevent)
{
Button button = (Button)changeevent.getSource();
button.repaint();
}
(轉載完)
通過這些相信大家能夠感受到Swing的設計是MVC的典範,這是一點都不假的。下一節將要看看在基于B/S的應用中MVC模式是如何被應用的。
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
前言
之所以說是再戰,是因爲在沒有學習設計模式之前已經基于MVC體系結構做過一些項目,主要是小項目,當初理解MVC有一些困難。現在已經把Gof所說的相對簡單但是最常見的這些設計模式:
Abstract Factory
Adapter
Composite
Decorator
Factory Method
Observer
Strategy
Template Method
都詳細過了一遍了,而我們都知道MVC是一種比較特殊的模式,MVC並不屬于GOF的23個設計模式之列,但是Smalltalk的MVC框架結構在著名的GOF書中作爲一個重要的例子被提出來,並給予了很高的評價。一般的來講,我們認爲GOF的23個模式是一些中級的模式,在它下面還可以抽象出一些更爲一般的低層的模式,在其上也可以通過組合來得到一些高級的模式,即架構模式。MVC就可以看作是一些模式進行組合之後的結果(實際上,MVC的出現要早于設計模式的提出,因而只是對它在設計模式的基礎上進行在分析)。所以說現在有必要站在設計模式的思想上來看MVC模式。<<Java與模式>>在MVC模式與用戶輸入數據檢查這個專題裏大量地談到MVC模式跟設計模式的關系,本文按這個專題的思路以筆記的形式展開。
MVC模式可以分解爲以下設計模式
在GOF書的Introduction中,有一小節是“Design Patterns in Smalltalk MVC”即介紹在MVC模式裏用到的設計模式。它大概向我們傳達了這樣的信息:合成模式+策略模式+觀察者模式約等于MVC模式(當然MVC模式要多一些東西)。也就是說它在大部分情況下是下面幾個模式:
1 觀察者模式
類圖結構在Gof裏的表示如下:
[img]http://images.wangchao.net.cn/images/upload/images/lsdn/1190408558567.jpg[/img]
2 合成模式
類圖結構在Gof裏的表示如下:
[img]http://images.wangchao.net.cn/images/upload/images/lsdn/1190408558677.png[/img]
3 策略模式
[img]http://images.wangchao.net.cn/images/upload/images/lsdn/1190408558802.png[/img]
除了這三種主要的設計模式,Gof最後也提到了MVC還使用了其它的設計模式,比如說工廠方法模式用來說明對一個view的默認的控制器,還有裝飾模式用來爲view增加一個滾動條等。但是在MVC模式裏起主要作用的還是前面列出來的三個設計模式。
模式的分類
書中提到由于[GOF95]是論述軟件模式的著作的第一本,也是OO設計理論著作中最流行的一本,因此有些人常常使用設計模式(Design Pattern)一詞來指所有直接處理軟件架構,設計,程序實現的任何種類的模式。另外一種說法是強調要劃分三種不同層次的模式:架構模式(Architectual Pattern),設計模式(Design Pattern),成例(Idiom)。成例有時稱爲代碼模式(Codeing Pattern)。
架構模式(Architectual Pattern)
面向對象軟件大師Martin Fowler有本書叫做<< Patterns of Enterprise Application Architecture>>中文名叫<<企業應用架構模式>>。這本書就是關于如何將企業應用分層,以及如何組織各層工作的。例如這本書在第二部分深入講解模式時,在談到了Web表示層的時候就分析了MVC模式,從這些我們可以知道架構模式提供一些事先定義好的子系統,指定他們的責任,並給出把它們組織在一起的法則和指南,一個架構模式常常可以分解成很多個設計模式的聯合使用。
設計模式
設計模式描述在軟件設計過程中普遍存在相互通信的組件中重複出現的結構,這種結構解決在一定的背景中的具有一般性的設計問題。
代碼模式或成例(Coding Pattern或Idiom)
代碼模式(或成例)是較低層次的模式,並與編程語言密切相關。代碼模式描述怎樣利用一個特定的編程語言的特點來實現一個組件的某些特定的方面或關系。
從上面的敘述中其實我們不難從一個軟件系統中找到他們,因爲三種不同的模式存在于它們自的抽象層次和具體層次上,所以少了其中任何一種模式,或者有一種設計的不好,都會給整個軟件系統帶來害處,如可維護性低等缺點。因爲架構模式是一個系統的高層次策略,涉及到大尺度的組件以及整體性質和力學,它設計的好壞可以影響到總體布局和框架性結構。設計模式是中等尺度的結構策略。這些中等尺度的結構實現了一些大尺度組件的行爲和它們之間的關系,所以設計模式的好壞不會影響到系統的總休布局和總體框架,但是它會影響到系統的擴展性,維護性等方面。代碼模式是特定的範例和與特定編程語言有關的編程技巧。它不會影響到一個部件或子系統的中等尺度的結構,更不會影響到系統的總體布局和大尺度框架,但是會影響到一個中等尺度組件的內部,外部的結構或行爲的底層細節。
談MVC架構模式
MVC中的三個角色
[img]http://images.wangchao.net.cn/images/upload/images/lsdn/1190408559005.png[/img]
Model(模型端)
Mod封裝的是數據源和所有基于對這些數據的操作。在一個組件中,Model往往表示組件的狀態和操作這些狀態的方法,往往是一系列的公開方法。通過這些公開方法,便可以取得模型端的所有功能。
在這些公開方法中,有些是取值方法,讓系統其他部分可以得到模型端的內部狀態參數,其他的改值方法則允許外部修改模型端的內部狀態。模型端還必須有方法登記視圖,以便在模型端的內部狀態發生變化時,可以通知視圖端。我們可以自己定義一個Subject接口來提供登記和通知視圖所需的接口或者繼承Java.util.Observable類,讓父類完成這件事。
多個View(視圖端)
View封裝的是對數據源Model的一種顯示。一個模型可以由多個視圖,並且可以在需要的時候動態地登記上所需的視圖。而一個視圖理論上也可以同不同的模型關聯起來。
在前言裏提到了,MVC模式用到了合成模式,這是因爲在視圖端裏,視圖可以嵌套,比如說在一個JFrame組件裏面,可以有菜單組件,很多按鈕組件等。
多個Controller(控制器端)
封裝的是外界作用于模型的操作。通常,這些操作會轉發到模型上,並調用模型中相應的一個或者多個方法(這個方法就是前面在介紹模型的時候說的改值方法)。一般Controller在Model和View之間起到了溝通的作用,處理用戶在View上的輸入,並轉發給Model來更改其狀態值。這樣Model和View兩者之間可以做到松散耦合,甚至可以彼此不知道對方,而由Controller連接起這兩個部分。也在前言裏提到,MVC用到了策略模式,這是因爲View用一個特定的Controller的實例來實現一個特定的響應策略,更換不同的Controller,可以改變View對用戶輸入的響應。
MVC模式用在基于C/S的應用
盡管MVC設計模式通常是用來設計整個用戶界面(GUI)的,JFC的設計者們卻獨創性的把這種設計模式用來設計Swing中的單個的組件(Component),例如表格Jtable等。下面的討論也就是對于Swing單個組件所體現出來的這種MVC模式來說的。
嚴格地講,Swing中的MVC實際上是MVC的一個變體:M-VC。 拿JTable來說,MVC 就是 TableModel JTable TableUI. Swing中只顯示的定義了Model接口(TableModel),而在一個UI對象中集成了視圖和控制器的部分機制(JTable)。View和Control比較松散的交叉組合在一起,而更多的控制邏輯是在事件監聽者部分引入的爲了更好地展現Swing是一個設計優秀的Java包,充滿了大師的智慧,讓我們更加深入的進行分析,我將采用最常見的組件button來說明。
注:以下例子是摘自網上一篇名爲“通過Java Swing看透MVC設計模式”的佚名文章,感覺對從Swing庫的設計來看MVC模式的應用有相當大的幫助,故插到此處同大家分享,黑色字迹部分爲自己的說明。
(轉載開始)
Model的設計
一個按鈕的model所應該具備的行爲由一個接口ButtonModel來完成。一個按鈕model實例封裝了其內部的狀態,並且定義了按鈕的行爲。它的所有方法可以分爲四類:
1、查詢內部狀態
2、操作內部狀態
3、添加和刪除事件監聽器
4、發生事件
程序員通常並不會直接和model以及view/controller打交道,他們通常隱藏于那些繼承自java.awt.Component的組件裏面了,這些組件就像膠水一樣把MVC三者合三爲一。也正是由于這些繼承的組件對象,一個程序員可以很方便的混合使用Swing組件和AWT組件,然後,我們知道,Swing組件有很多都是直接繼承自相應的AWT組件,它能提供比AWT組件更加方便易用的功能,所以通常情況下,我們沒有必要混合使用兩者。
一個實例
現在我們已經明白了Java類與MVC各個部分的對應關系,我們可以更加深入一點去分析問題了。下面將要講述一個小型的使用MVC模式開發的例子。因爲JFC十分的複雜,我只能把我的例子局限于一個用戶界面組件裏面(如果你猜是一個按鈕的例子,那麽你對了!)
讓我們來看看這個例子的所有部分吧。
Button組件
最顯而易見的開始的地方就是代表了按鈕組件本身的代碼,因爲這個類是大部分程序員會接觸的。
就像我前面提到的,按鈕用戶界面組件類實際上就是model和view/controller的之間的黏合劑。每個按鈕組件都和一個model以及一個controller關聯,model定義了按鈕的行爲,而view/controller定義了按鈕的表現。而應用程序可以在任何事件改變這些關聯。讓我們看看得以實現此功能的代碼。
注:以下代碼是我從JDK1.4源代碼AbstractButton類中提取出來的,因爲查看Jbutton源代碼(再次提供[url=http://javaresearch.gro.clinux.org/jdk140/index.html]JDK1.4在線源代碼地址[/url])我們可以發現,它是繼承自AbstractButton類的,所以下面的代碼是來自AbstractButton的,跟原文有點不一樣:
1. /**
2.
3. * Sets the model that this button represents.
4.
5. * @param m the new <code>ButtonModel</code>
6.
7. * @see #getModel
8.
9. * @beaninfo
10.
11. * bound: true
12.
13. * description: Model that the Button uses.
14.
15. */
16.
17. public void setModel(ButtonModel newModel) {
18.
19.
20.
21. ButtonModel oldModel = getModel();
22.
23.
24.
25. if (oldModel != null) {
26.
27. oldModel.removeChangeListener(changeListener);
28.
29. oldModel.removeActionListener(actionListener);
30.
31. changeListener = null;
32.
33. actionListener = null;
34.
35. }
36.
37.
38.
39. model = newModel;
40.
41.
42.
43. if (newModel != null) {
44.
45. changeListener = createChangeListener();
46.
47. actionListener = createActionListener();
48.
49. itemListener = createItemListener();
50.
51. newModel.addChangeListener(changeListener);
52.
53. newModel.addActionListener(actionListener);
54.
55. newModel.addItemListener(itemListener);
56.
57. mnemonic = newModel.getMnemonic();
58.
59. } else {
60.
61. mnemonic = '\0';
62.
63. }
64.
65.
66.
67. updateDisplayedMnemonicIndex(getText(), mnemonic);
68.
69.
70.
71. firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, newModel);
72.
73. if (newModel != oldModel) {
74.
75. revalidate();
76.
77. repaint();
78.
79. }
80.
81. }
注:你可以多花一些時間按我上面提供的鏈接地址來仔細閱讀一下Button類及其它相關類的源代碼。
ButtonModel類
ButtonModel維護著三種類型的狀態信息:是否被按下(pressed),是否“武裝上了”(armed),是否被選擇(selected)。它們都是boolean類型的值。
一個按鈕被按下(pressed)是指當鼠標在按鈕上面的時候,按下鼠標但是還沒有松開鼠標按鈕的狀態,即使用戶此時把鼠標拖拽到按鈕的外面也沒有改變這種狀態。
一個按鈕是否“武裝了”(armed)是指按鈕被按下,並且鼠標還在按鈕的上面。
一些按鈕還可能被選擇(selected),這種狀態通過重複的點擊按鈕取得true或者false的值。
注:查看ButtonModel源代碼我們可以看到它是一個接口,定義了一組方法:
public interface ButtonModel extends ItemSelectable {
boolean isArmed();
boolean isPressed();
…
boolean isRollover();
void addActionListener(ActionListener l);
void removeActionListener(ActionListener l);
void addItemListener(ItemListener l);
…
void removeChangeListener(ChangeListener l);
其實看到這裏,如果你再回頭看下這個例子剛開始時“Model的設計”以及前面的內容,就會明白什麽是“Mod封裝的是數據源和所有基于對這些數據的操作。在一個組件中,Model往往表示組件的狀態和操作這些狀態的方法,往往是一系列的公開方法。通過這些公開方法,便可以取得模型端的所有功能”
我們再來看看AbstractButton的源代碼:
1. public abstract class implements ItemSelectable, SwingConstants { AbstractButton extends JComponent
2. protected ButtonModel model= null;
3. protected ChangeListener changeListener = null;
4. protected ActionListener actionListener = null;
5. protected ItemListener itemListener = null;
6. protected transient ChangeEvent changeEvent;
7. public boolean isSelected() {
8. return model.isSelected();
9. }
10.public void doClick() {
11.doClick(68);
12. }
13.public void doClick(int pressTime) {
14. Dimension size = getSize();
15.model.setArmed(true);
16. model.setPressed(true);
17. paintImmediately(new Rectangle(0,0, size.width, size.height));
18.try {
19.Thread.currentThread().sleep(pressTime);
20. } catch(InterruptedException ie) {
21.}
22.model.setPressed(false);
23.model.setArmed(false);
24.}
25.//其它代碼
26.}
上面的代碼setArmed等方法是由DefaultButtonModel定義的,它是ButtonModel接口的一個默認實現。我們如果繼承了DefaultButtonModel,這樣可以覆蓋缺省的狀態定義,實現有個性的按鈕。ButtonUI類
按鈕的view/controller是負責構建表示層的。缺省情況下它僅僅是用背景色畫一個矩形而已,他們的子類繼承了他們並且覆蓋了繪制的方法,使得按鈕可以有許多不同的表現,例如MOTIF,Windows 95,Java樣式等等。
注:在javax.swing.plaf這個包裏我們可以找到ButtonUI,這是一個抽象類,類內部是空的,而在javax.swing.plaf.basic這個包裏我們可以找到BasicButtonUI,它繼承自ButtonUI,其實我們觀察下包結構會發現有javax.swing.plaf.metal,javax.swing.plaf.multi等,這幾個就是來控制Button在不同系統間的不同的顯示效果。:
public void update(Button button, Graphics graphics)
{
}
public void paint(Button button, Graphics graphics)
{
Dimension dimension = button.getSize();
Color color = button.getBackground();
graphics.setColor(color);
graphics.fillRect(0, 0, dimension.width, dimension.height);
}
ButtonUI類並不自己處理AWT事件,他們會使用一個定制的事件監聽器把低級的AWT事件翻譯爲高級的Button模型期望的語義事件。下面就是安裝/卸載事件監聽器的代碼。
private static ButtonUIListener buttonuilistener = null;
public void uninstallUI(Button button)
{
button.removeMouseListener(buttonuilistener);
button.removeMouseMotionListener(buttonuilistener);
button.removeChangeListener(buttonuilistener);
}
View/Controller實際上就是一些方法。他們不維護任何自己的狀態信息。因此,許多按鈕的實例可以共享一個ButtonUI實例。ButtonUI是通過在方便的參數列表裏面加上按鈕的引用來區分各個不同的按鈕。
ButtonUIListener類
ButtonUIListener類可以幫助Button類去轉變鼠標或者鍵盤的輸入爲對按鈕模型的操作。這個監聽器類實現了:MouseListener,MouseMotionListener,ChangeListener接口,並且處理一下事件:
public void mouseDragged(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
if (buttonmodel.isPressed())
{
if (button.getUI().contains(button, mouseevent.getPoint()))
{
buttonmodel.setArmed(true);
}
else
{
buttonmodel.setArmed(false);
}
}
}
public void mousePressed(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
buttonmodel.setPressed(true);
buttonmodel.setArmed(true);
}
public void mouseReleased(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
buttonmodel.setPressed(false);
buttonmodel.setArmed(false);
}
public void stateChanged(ChangeEvent changeevent)
{
Button button = (Button)changeevent.getSource();
button.repaint();
}
(轉載完)
通過這些相信大家能夠感受到Swing的設計是MVC的典範,這是一點都不假的。下一節將要看看在基于B/S的應用中MVC模式是如何被應用的。