轉載自https://www.ibm.com/developerworks/cn/java/j-lo-puremvc/index.html?ca=drs-html
MVC 是一個很是常見的設計模式,被普遍運用於各類和用戶交互的軟件中,像 java Swing 類庫和 SWT、J2EE 中的 JSP、wxWidgets、QT 和 Gnome 等。能夠很不誇張的說,幾乎全部和 UI 有關的類庫,不論什麼語言,什麼平臺都或多或少有 MVC 的影子。可是通過這麼多年的發展,MVC 這個設計模式已經由最初 smalltalk 中的樣子發生了不少變化,除了數目讓人眼花繚亂外,不少 MVC 模型之間已經有了很大差別,不一樣的人提及 MVC 時,除了基本原理外,摻雜了不少平臺或者語言相關的東西,常常會引發不少誤解。PureMVC 是一個跨平臺跨語言的 MVC 通用框架,這個框架基於 MVC 模式設計,清晰簡單,瞭解這個框架會有助於從新認識 MVC 設計模式自己,或者可以提供一個基礎讓大多數人可以更清晰的交流基於 MVC 的設計方案。java
回頁首數據庫
PureMVC 是在基於模型、視圖和控制器 MVC 模式創建的一個輕量級的應用框架,這是一個開源框架,它最初是被用於 ActionScript 3 語言使用的 Adobe Flex、Flash 和 AIR 之上,如今已經移植到幾乎全部主要的軟件平臺之上見表 1。編程
Language | Targets |
---|---|
ActionScript 2 | Flex 1.5, Flash 8, FlashLite |
ActionScript 3 | Flex 2, 3, Flash 9/CS3, AIR. |
C# | .NET 1.0/2.0 Silverlight, Windows Mobile and Pocket PC. |
ColdFusion | ColdFusion 8 |
haXe | JavaScript, Flash 8, Flash 9 and the Neko VM. |
Java | Java Mobile, Standard and Enterprise Editions (ME, SE, EE), JavaFX, Servlets, Applets, and GWT |
JavaScript | Browser neutral |
Objective C | Apple iPhone and Mac |
PHP | PHP 5 |
Python | Python 2.5 for wxPython, Google App Engine, Pyjamas [13] |
Ruby | |
C++ | MSVC 8.0/9.0/10.0, MinGW 3.4.5, GNU G++, Embarcadero C++ 6.21 |
PureMVC 框架有兩個分支版本:標準和多核。標準版提供了一種簡單的編碼分離的方法,按照基本的 MVC 概念設計而成。多核版本支持模塊化編程,容許多個 PureMVC 應用運行在一塊兒。本文咱們主要介紹標準版的功能和使用。設計模式
回頁首mvc
在 MVC 模式中,應用程序被分爲低耦合的三層:Model、View 和 Controller。PureMVC 在此基礎上有所擴充,經過模塊化設計,致力於進一步下降模塊間耦合性,建立了一個易於擴展限制很小的通用框架,圖一是 PureMVC 的設計圖。框架
(查看圖 1 的 清晰版本。)ide
在 PureMVC 實現的經典 MVC 元設計模式中,Model、View 和 Controller 分別由一個單例類來管理,合稱爲核心層或核心角色。 另外,在 PureMVC 中還提供了一個單例類 —— Façade,主要做用是做爲與核心層通訊的惟一接口,簡化開發複雜度。模塊化
從上圖中能夠看出,除了這幾個主要對象之外,框架還有以下類 Proxy、Mediator 和 Command,如下簡單介紹一下他們的做用。函數
除了基本的對象結構之外,爲了解耦合,PureMVC 框架中引入了事件機制,這是個很是簡單觀察者設計模式,全部的事件都是一個 Notification,不一樣對象之間經過 Notification 來同步操做和交換信息。例如若是想更新界面中某個 Mediator,首先咱們定義 Notification 用於此目的,而後註冊 Mediator 監聽該 Notification,而後就能夠在程序中任何地方生成一個 Notification,經過事件機制,Mediator 就會接收到 Notification,而後更新須要的部分。整個過程 Mediator 只和 Notification 有關,沒有其餘依賴,有效的下降了對象之間的依賴程度。
介紹完 PureMVC 的基本結構和事件模型,咱們來看看一個典型的 PureMVC 應用如何構造。首先實際的應用程序都有一個 Façade 子類,這個 Façade 類對象負責初始化 Controller(控制器),創建 Command 與 Notification 名之間的映射,並執行一個 Command 註冊全部的 Model 和 View,一旦初始化的工做作完,對象之間的關係創建好之後,應用程序就能夠正常運行了。
首先咱們看看 PureMVC Java 標準的核心類圖,如圖 2 所示:
核心類的實現徹底體現了框架的設計意圖,由 IController、IView 和 IModel 三個接口來規範 MVC 三種角色的行爲,同時又提供了三個默認的實現類。
而後咱們在看看輔助性類的類圖:
(查看圖 3 的 清晰版本。)
從中能夠看出 IFaçade 是整個程序的入口和負責管理全部對象的類,其中能夠註冊 proxy、command 和 Mediator,經過 IFaçade 的子類,系統的各類資源獲得統一管理和調度。
而後能夠看到 INotification 是整個事件驅動模型的核心類,經過事件模型,整個應用程序的各個部分的耦合度變得很是低,同時用戶能夠自定義 INotification 的子類,從而實現最大的可定製性。
下面咱們用 PureMVC 來實現一個典型的應用程序通信錄,這個程序由於只是示範,因此只實現了基本的編輯和管理功能,下圖是程序運行時的截圖。
當用戶填寫正確的用戶名和密碼的時候,就顯示登錄成功的消息:
當用戶填寫錯誤的用戶名和密碼的時候,就顯示登錄不成功的消息:
首先,咱們來看看程序的入口代碼,即 main 函數所在的類 LoginFacade,這個類是 Façade 的子類,同時繼承了 IFacade 接口。
package login; import org.puremvc.java.patterns.facade.Facade; import org.puremvc.java.patterns.observer.Notification; public class LoginFacade extends Facade { public static final String STARTUP = "startup"; public static final String SUBMIT_LOGIN = "submitLogin"; public static final String LOGIN_SUCCESSFUL = "loginSuccessful"; public static final String LOGIN_FAIL = "loginFail"; public static LoginFacade getInstance(){ if (instance == null) instance = new LoginFacade(); return (LoginFacade)instance; } protected void initializeController() { super.initializeController(); registerCommand(STARTUP, StartupCommand.class); registerCommand(SUBMIT_LOGIN, LoginCommand.class); } @Override protected void initializeModel() { super.initializeModel(); registerProxy(new UsersProxy()); } @Override protected void initializeView() { super.initializeView(); registerMediator(new LoginScreenMediator()); } public static void main(String[] args) { getInstance().notifyObservers(new Notification(STARTUP, null, null)); } }
LoginFacade 覆蓋父類的三個初始化方法:initializeController、initializeModel 和 initializeView,做爲單例,經過父類的構造函數分別調用了這三個函數,從而完成初始化工做。三個函數也很簡單,只是在 initializeController 中註冊全部的 Command 類,在 initializeModel 中註冊全部的 Proxy 類,在 initializeView 中註冊全部 Mediator 類。
Main 函數很簡單只是發送了一個 STARTUP 的消息,該消息會被 StartupCommand 類接受,從而執行相應的初始化動做。
而後,咱們來看 StartupCommand 類,這個類很簡單,在 StartupCommand 中只是設置了 lookandfeel,而後就調用系統中已經註冊了的 LoginScreenMediator 類的對象,而後讓其顯示在桌面上,限於 Swing 的 EDT 特性,全部這些工做都放在 SwingUtilities.invokeLater 中執行。
本例中 StartupCommand 沒有執行太複雜的工做,可是實際運行的系統,就能夠根據須要在 StartupCommand 中各類須要執行的初始化工做。
package login; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import org.puremvc.java.interfaces.INotification; import org.puremvc.java.patterns.command.SimpleCommand; public class StartupCommand extends SimpleCommand implements Runnable{ @Override public void run() { try { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); break; } } LoginScreenMediator mediator = (LoginScreenMediator) this.facade. retrieveMediator(LoginScreenMediator.NAME); mediator.show(); } catch (Exception e) {} } @Override public void execute(INotification notification) { SwingUtilities.invokeLater(this); } }
下面咱們接着看 LoginScreenMediator 類。
package login; import java.awt.event.*; import javax.swing.*; import org.puremvc.java.interfaces.INotification; import org.puremvc.java.patterns.mediator.Mediator; import org.puremvc.java.patterns.observer.Notification; public class LoginScreenMediator extends Mediator implements ActionListener{ public static final String NAME = "LoginScreenMediator"; private LoginScreen dialog; public LoginScreenMediator() { super(NAME, null); } public void show(){ dialog = new LoginScreen(new javax.swing.JFrame(), true); dialog.addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent e) { System.exit(0); } }); dialog.btnLogin.addActionListener(this); dialog.setVisible(true); } public String[] listNotificationInterests() { return new String[] { LoginFacade.LOGIN_SUCCESSFUL, LoginFacade.LOGIN_FAIL }; } public void handleNotification(INotification note) { String noteName = note.getName(); if (LoginFacade.LOGIN_SUCCESSFUL.equals(noteName)) { JOptionPane.showMessageDialog(null, "Login ok!"); System.exit(0); } else if (LoginFacade.LOGIN_FAIL.equals(noteName)) { JOptionPane.showMessageDialog(null, "Login failed, try again"); } } @Override public void actionPerformed(ActionEvent e) { String name = dialog.userName.getText(); String pass = new String(dialog.password.getPassword()); LoginVO userInfo = new LoginVO( name, pass ); this.facade.notifyObservers(new Notification(LoginFacade.SUBMIT_LOGIN, userInfo, null)); } }
通常來講,Mediator 這個類負責管理視圖,即 Swing 控件或者控件組,同時響應用戶的操做。本例中的 LoginScreenMediator 管理了 LoginScreen 這個 Swing 控件。LoginScreenMediator 的 show 函數的工做首先初始化 LoginScreen,LoginScreen 就是咱們顯示給用戶看的 JDialog 子類,其中有兩個 JLabel,一個 JText Field,一個 JPassword Field 和一個按鈕,這裏就不列出 LoginScreen 的代碼,能夠在附件中的源碼查看詳細代碼。
LoginScreenMediator 的 show 函數而後註冊了 Login 按鈕的 ActionEvent 事件,而後就顯示了 LoginScreen 控件。到此爲止,用戶界面的顯示工做就完成了。LoginScreenMediator 類中另一個須要注意的地方是 listNotificationInterests,在這個方法中該類說明了它所關注的 Notification:LOGIN_SUCCESSFUL 和 LOGIN_FAIL。之後一旦有出現了這兩個 Notification,LoginScreenMediator 類的 handleNotification 函數就會被調用。
下面咱們來看用戶事件處理部分,界面顯示以後,一旦用戶點擊填入用戶名和密碼,而後點擊 Login 按鈕,就會觸發 LoginScreenMediator 的 public void actionPerformed(ActionEvent e) 函數,在這個函數中只是獲取用戶名和密碼,而後把獲得的信息,經過 SUBMIT_LOGIN 這個 Notification 發送出去,由於本類的工做只是負責管理 View,具體如何驗證登錄信息則不是它關注的範圍,天然有其餘 Command 處理。
在前面的 LoginFacade 中註冊了一個 LoginCommand 類來處理 SUBMIT_LOGIN 這種類型的 Notification,下面是 LoginCommand 的源碼。
package login; import org.puremvc.java.interfaces.INotification; import org.puremvc.java.patterns.command.SimpleCommand; public class LoginCommand extends SimpleCommand { @Override public void execute(INotification notification) { UsersProxy usersProxy = (UsersProxy) his.facade.retrieveProxy(UsersProxy.NAME); LoginVO userInfo = (LoginVO) notification.getBody(); if(usersProxy.checkLogin(userInfo)){ sendNotification(LoginFacade.LOGIN_SUCCESSFUL, null, null); } else { sendNotification(LoginFacade.LOGIN_FAIL, null, null); } } }
LoginCommand 接收到 Notification 以後,首先獲取其中的附加信息(用戶名和密碼),而後經過系統中已經註冊的 UsersProxy 來檢查用戶名和密碼是否,若是正確,就發送 LOGIN_SUCCESSFUL 的 Notification,不然就發送 LOGIN_FAIL 的 Notification。本例中 UsersProxy 的功能很是簡單,只是檢查一個內存中 map,看看接收的用戶名和密碼,是否和已經保存在 map 中預設值數據是否一致,實際上,這裏也能夠經過檢查 LDAP 或者數據庫之類的 UserStore 來執行,效果是相似的。
下面咱們看 LOGIN_SUCCESSFUL 和 LOGIN_FAIL 的 Notification 的處理,前面提到 LoginScreenMediator 類關注這兩個 Notification,因此它會在 handleNotification 函數中處理這兩個 Notification,限於示例,程序的功能就只是給用戶提示一個登錄成功或失敗的消息。
以上就是演示代碼的全部功能,從基本的代碼結構來看,類比較多,可是你們能夠看到每一個類都很簡單,職責也很清楚,同時每一個 Command、Proxy 和 Mediator 類之間的耦合度很小,Mediator 和 Proxy 甚至都不知道對方的存在,也依賴 Command 類,這樣一來,Proxy 和 Mediator 的複用程度變高了。
另外 Notification 機制,你們能夠看到程序有很強的相似工做流的性質,其實在實際使用過程當中,徹底能夠根據需求分析的結果,結合工做流來定義 Notification。 Notification 和 Swing 事件不同,它不關注於用戶的鍵盤等 UI 事件,它關注業務流程,因此也是一個很是有力幫助需求人員和設計人員之間的溝通。
PureMVC 框架做爲一個成熟的通用框架,具備如下優勢:
從上面的介紹中,咱們能夠看到,在一個項目中根據實際狀況整合 PureMVC 框架,可讓可使得和 UI(不限於 Swing)相關部分設計更簡潔,並提供更多的可複用組件,加強需求和設計之間的溝通效果,有效提升項目的設計質量。