转载自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)相关部分设计更简洁,并提供更多的可复用组件,加强需求和设计之间的沟通效果,有效提升项目的设计质量。