文章詳情頁(yè)
通過(guò)JAVA SWING看透MVC設(shè)計(jì)模式
瀏覽:31日期:2024-06-29 08:54:20
內(nèi)容: 來(lái)自:http://www.javaworld.com 作者:vipcowrie(翻譯) 一個(gè)好的用戶界面(GUI)的設(shè)計(jì)通??梢栽诂F(xiàn)實(shí)世界找到相應(yīng)的表現(xiàn)。例如,如果在您的面前擺放著一個(gè)類似于電腦鍵盤按鍵的一個(gè)簡(jiǎn)單的按鈕,然而就是這么簡(jiǎn)單的一個(gè)按鈕,我們就可以看出一個(gè)GUI設(shè)計(jì)的規(guī)則,它由兩個(gè)主要的部分構(gòu)成,一部分使得它具有了按鈕應(yīng)該具有的動(dòng)作特性,例如可以被按下。另外一部分則負(fù)責(zé)它的表現(xiàn),例如這個(gè)按鈕是代表了A還是B。 看清楚這兩點(diǎn)你就發(fā)現(xiàn)了一個(gè)很強(qiáng)大的設(shè)計(jì)方法,這種方法鼓勵(lì)重用reuse,而不是重新設(shè)計(jì)redesign。你發(fā)現(xiàn)按鈕都有相同的機(jī)理,你只要在按鈕的頂上噴上不同的字母便能制造出“不同的按鈕,而不用為了每個(gè)按鈕而重新設(shè)計(jì)一份圖紙。這大大減輕了設(shè)計(jì)工作的時(shí)間和難度。 如果您把上述設(shè)計(jì)思想應(yīng)用到軟件開(kāi)發(fā)領(lǐng)域,那么取得相似的效果一點(diǎn)都不讓人驚奇。一個(gè)在軟件開(kāi)發(fā)領(lǐng)域應(yīng)用的非常廣泛的技術(shù)Model/View/Controller(MVC)便是這種思想的一個(gè)實(shí)現(xiàn)。 這當(dāng)然很不錯(cuò),但是或許您又開(kāi)始疑惑這和java基礎(chǔ)類JFC(Java Foundation Class)中的用戶界面設(shè)計(jì)部分(Swing)又有什么關(guān)系呢?好的,我來(lái)告訴你。 盡管MVC設(shè)計(jì)模式通常是用來(lái)設(shè)計(jì)整個(gè)用戶界面(GUI)的,JFC的設(shè)計(jì)者們卻獨(dú)創(chuàng)性的把這種設(shè)計(jì)模式用來(lái)設(shè)計(jì)Swing中的單個(gè)的組件(Component),例如表格Jtable,樹(shù)Jtree,組合下拉列表框JcomboBox等等等等。這些組件都有一個(gè)Model,一個(gè)View,一個(gè)Controller,而且,這些model,view,controller可以獨(dú)立的改變,就是當(dāng)組件正在被使用的時(shí)候也是如此。這種特性使得開(kāi)發(fā)GUI界面的工具包顯得非常的靈活。 好,來(lái)吧,讓我來(lái)告訴你它是如何工作的。 MVC設(shè)計(jì)模式就象我剛才指出的一樣,MVC設(shè)計(jì)模式把一個(gè)軟件組件區(qū)分為三個(gè)不同的部分,model,view,controller。 IMG http://www.csdn.net/Develop/ArticleImages/18/18953/CSDN_Dev_Image_2003-6-111007320.gif[/IMG]Model是代表組件狀態(tài)和低級(jí)行為的部分,它管理著自己的狀態(tài)并且處理所有對(duì)狀態(tài)的操作,model自己本身并不知道使用自己的view和controller是誰(shuí),系統(tǒng)維護(hù)著它和view之間的關(guān)系,當(dāng)model發(fā)生了改變系統(tǒng)還負(fù)責(zé)通知相應(yīng)的view。View代表了管理model所含有的數(shù)據(jù)的一個(gè)視覺(jué)上的呈現(xiàn)。一個(gè)Model可以有一個(gè)以上的View,但是Swing中卻很少有這樣的情況。Controller管理著model和用戶之間的交互的控制。它提供了一些方法去處理當(dāng)model的狀態(tài)發(fā)生了變化時(shí)的情況。使用鍵盤上的按鈕的例子來(lái)說(shuō)明一下:Model就是按鈕的整個(gè)機(jī)械裝置,View/Controller就是按鈕的表面部分。下面的圖解釋了如何把一個(gè)JFC開(kāi)發(fā)的用戶界面分為model,view,controller,注意,view/Controller被合并到了一起,這是MVC設(shè)計(jì)模式通常的用法,它們提供了組件的用戶界面(UI)。 IMG http://www.csdn.net/Develop/ArticleImages/18/18953/CSDN_Dev_Image_2003-6-111007322.gif[/IMG]用Button的例子詳細(xì)說(shuō)明為了更好的理解MVC設(shè)計(jì)模式和Swing用戶界面組件之間的關(guān)系,讓我們更加深入的進(jìn)行分析。我將采用最常見(jiàn)的組件button來(lái)說(shuō)明。我們從model來(lái)開(kāi)始。 Model一個(gè)按鈕的model所應(yīng)該具備的行為由一個(gè)接口ButtonModel來(lái)完成。一個(gè)按鈕model實(shí)例封裝了其內(nèi)部的狀態(tài),并且定義了按鈕的行為。它的所有方法可以分為四類:l 查詢內(nèi)部狀態(tài)l 操作內(nèi)部狀態(tài)l 添加和刪除事件監(jiān)聽(tīng)器l 發(fā)生事件其他的用戶界面組件有它們各自的與組件相關(guān)的Model,但是所有的組件Model都提供這四類方法。 View & Controller 上面的圖中講述一個(gè)按鈕的view/controller由一個(gè)接口ButtonUI完成。如果一個(gè)類實(shí)現(xiàn)了這個(gè)接口,那么它將會(huì)負(fù)責(zé)創(chuàng)建一個(gè)用戶界面,處理用戶的操作。它的所有方法可以被分為三大類:l 繪制Paintl 返回幾何類型的信息l 處理AWT事件其他用戶界面組件有他們自己的組件相關(guān)的View/Controller,但是他們都提供上述三類方法。 程序員通常并不會(huì)直接和model以及view/controller打交道,他們通常隱藏于那些繼承自java.awt.Component的組件里面了,這些組件就像膠水一樣把MVC三者合三為一。也正是由于這些繼承的組件對(duì)象,一個(gè)程序員可以很方便的混合使用Swing組件和AWT組件,然后,我們知道,Swing組件有很多都是直接繼承自相應(yīng)的AWT組件,它能提供比AWT組件更加方便易用的功能,所以通常情況下,我們沒(méi)有必要混合使用兩者。 一個(gè)實(shí)例 現(xiàn)在我們已經(jīng)明白了Java類與MVC各個(gè)部分的對(duì)應(yīng)關(guān)系,我們可以更加深入一點(diǎn)去分析問(wèn)題了。下面我們將要講述一個(gè)小型的使用MVC模式開(kāi)發(fā)的例子。因?yàn)镴FC十分的復(fù)雜,我只能把我的例子局限于一個(gè)用戶界面組件里面(如果你猜是一個(gè)按鈕的例子,那么你對(duì)了!) 讓我們來(lái)看看這個(gè)例子的所有部分吧。 Button類最顯而易見(jiàn)的開(kāi)始的地方就是代表了按鈕組件本省的代碼,因?yàn)檫@個(gè)類是大部分程序員會(huì)接觸的。 就像我前面提到的,按鈕用戶界面組件類實(shí)際上就是model和view/controller的之間的黏合劑。每個(gè)按鈕組件都和一個(gè)model以及一個(gè)controller關(guān)聯(lián),model定義了按鈕的行為,而view/controller定義了按鈕的表現(xiàn)。而應(yīng)用程序可以在任何事件改變這些關(guān)聯(lián)。讓我們看看得以實(shí)現(xiàn)此功能的代碼。 public void setModel(ButtonModel buttonmodel) { if (this.buttonmodel != null) { this.buttonmodel.removeChangeListener(buttonchangelistener); this.buttonmodel.removeActionListener(buttonactionlistener); buttonchangelistener = null; buttonactionlistener = null; } this.buttonmodel = buttonmodel; if (this.buttonmodel != null) { buttonchangelistener = new ButtonChangeListener(); buttonactionlistener = new ButtonActionListener(); this.buttonmodel.addChangeListener(buttonchangelistener); this.buttonmodel.addActionListener(buttonactionlistener); } updateButton(); } public void setUI(ButtonUI buttonui) { if (this.buttonui != null) { this.buttonui.uninstallUI(this); } this.buttonui = buttonui; if (this.buttonui != null) { this.buttonui.installUI(this); } updateButton(); } public void updateButton() { invalidate(); } 在進(jìn)入下一節(jié)之前,你應(yīng)該多花一些時(shí)間來(lái)仔細(xì)閱讀一下Button類的源代碼。 ButtonModel類 ButtonModel維護(hù)著三種類型的狀態(tài)信息:是否被按下(pressed),是否“武裝上了(armed),是否被選擇(selected)。它們都是boolean類型的值。 一個(gè)按鈕被按下(pressed)是指當(dāng)鼠標(biāo)在按鈕上面的時(shí)候,按下鼠標(biāo)但是還沒(méi)有松開(kāi)鼠標(biāo)按鈕的狀態(tài),及時(shí)用戶此時(shí)把鼠標(biāo)拖拽到按鈕的外面也沒(méi)有改變這種狀態(tài)。 一個(gè)按鈕是否“武裝了(armed)是指按鈕被按下,并且鼠標(biāo)還在按鈕的上面。 一些按鈕還可能被選擇(selected),這種狀態(tài)通過(guò)重復(fù)的點(diǎn)擊按鈕取得true或者false的值。 下面的代碼是狀態(tài)pressed的一個(gè)缺省的實(shí)現(xiàn)。狀態(tài)armed以及selected實(shí)現(xiàn)的代碼與之類似。ButtonModel類應(yīng)該被繼承,這樣可以覆蓋缺省的狀態(tài)定義,實(shí)現(xiàn)有個(gè)性的按鈕。 private boolean boolPressed = false; public boolean isPressed() { return boolPressed; } public void setPressed(boolean boolPressed) { this.boolPressed = boolPressed; fireChangeEvent(new ChangeEvent(button)); } 按鈕的模型button model還負(fù)責(zé)通知其他對(duì)象(事件監(jiān)聽(tīng)器)它們所感興趣的事件。從下面的代買中我們可以看出當(dāng)按鈕的轉(zhuǎn)臺(tái)發(fā)生改變的時(shí)候就會(huì)發(fā)出一個(gè)ChangeEvent。下面就是代碼: private Vector vectorChangeListeners = new Vector(); public void addChangeListener(ChangeListener changelistener) { vectorChangeListeners.addElement(changelistener); } public void removeChangeListener(ChangeListener changelistener) { vectorChangeListeners.removeElement(changelistener); } protected void fireChangeEvent(ChangeEvent changeevent) { Enumeration enumeration = vectorChangeListeners.elements(); while (enumeration.hasMoreElements()) { ChangeListener changelistener = (ChangeListener)enumeration.nextElement(); changelistener.stateChanged(changeevent); } }在進(jìn)入下一節(jié)之前,你應(yīng)該多花一些時(shí)間來(lái)仔細(xì)閱讀一下ButtonModel類的源代碼。 ButtonUI類 按鈕的view/controller是負(fù)責(zé)構(gòu)建表示層的。缺省情況下它僅僅是用背景色畫一個(gè)矩形而已,他們的子類繼承了他們并且覆蓋了繪制的方法,使得按鈕可以有許多不同的表現(xiàn),例如MOTIF,Windows 95,Java樣式等等。 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事件,他們會(huì)使用一個(gè)定制的事件監(jiān)聽(tīng)器把低級(jí)的AWT事件翻譯為高級(jí)的Button模型期望的語(yǔ)義事件。下面就是安裝/卸載事件監(jiān)聽(tīng)器的代碼。 private static ButtonUIListener buttonuilistener = null; public void installUI(Button button) { button.addMouseListener(buttonuilistener); button.addMouseMotionListener(buttonuilistener); button.addChangeListener(buttonuilistener); } public void uninstallUI(Button button) { button.removeMouseListener(buttonuilistener); button.removeMouseMotionListener(buttonuilistener); button.removeChangeListener(buttonuilistener); }View/Controller實(shí)際上就是一些方法。他們不維護(hù)任何自己的狀態(tài)信息。因此,許多按鈕的實(shí)例可以共享一個(gè)ButtonUI實(shí)例。ButtonUI是通過(guò)在方面的參數(shù)列表里面加上按鈕的引用來(lái)區(qū)分各個(gè)不同的按鈕。 同樣,希望你能多花一些時(shí)間來(lái)看看ButtonUI類,然后咱們進(jìn)入下一節(jié)。 ButtonUIListener類 ButtonUIListener類可以幫助Button類去轉(zhuǎn)變鼠標(biāo)或者鍵盤的輸入為對(duì)按鈕模型的操作。這個(gè)監(jiān)聽(tīng)器類實(shí)現(xiàn)了: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(); }在進(jìn)入下一節(jié)之前希望你能仔細(xì)閱讀ButtonUIListener的源代碼。 總結(jié)我希望你能按照上面講述的方法去做。如果不能,那么所有的努力都將白費(fèi)。這個(gè)例子以及Swing用戶界面組件的好處在于你不用去花時(shí)間去弄明白他們底層是如何設(shè)計(jì)實(shí)現(xiàn)的就可以很方便的使用他們了。他們都提供了缺省的model以及view/controller,然后,當(dāng)你自己做組件的時(shí)候,你會(huì)發(fā)現(xiàn)上面的思想的強(qiáng)大之處。 Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd
標(biāo)簽:
Java
相關(guān)文章:
1. PHP 面向?qū)ο蟪绦蛟O(shè)計(jì)之類屬性與類常量實(shí)現(xiàn)方法分析2. 通過(guò)工廠模式返回Spring Bean方法解析3. Python Selenium破解滑塊驗(yàn)證碼最新版(GEETEST95%以上通過(guò)率)4. python 通過(guò)exifread讀取照片信息5. php設(shè)計(jì)模式之代理模式分析【星際爭(zhēng)霸游戲案例】6. 《CSS3實(shí)戰(zhàn)》筆記--漸變?cè)O(shè)計(jì)(二)7. PHP設(shè)計(jì)模式入門之狀態(tài)模式原理與實(shí)現(xiàn)方法分析8. Vue中通過(guò)vue-router實(shí)現(xiàn)命名視圖的問(wèn)題9. Spring Boot 通過(guò) Mvc 擴(kuò)展方便進(jìn)行貨幣單位轉(zhuǎn)換的代碼詳解10. SpringBoot通過(guò)源碼探究靜態(tài)資源的映射規(guī)則實(shí)現(xiàn)
排行榜
