PureMVC Java 版本的基本结构

PureMVC 样例程序 —— 用户登陆

下面我们用 PureMVC 来实现一个典型的应用程序通讯录,这个程序因为只是示范,所以只实现了基本的编辑和管理功能,下图是程序运行时的截图。


图 4. 主界面
图 4. 主界面  

当用户填写正确的用户名和密码的时候,就显示登陆成功的消息:


图 5. 登陆成功
图 5. 登陆成功  

当用户填写错误的用户名和密码的时候,就显示登陆不成功的消息:


图 6. 登陆失败
图 6. 登陆失败  

首先,我们来看看程序的入口代码,即 main 函数所在的类 LoginFacade,这个类是 Façade 的子类,同时继承了 IFacade 接口。


清单 1. LoginFacade.java 的源码
				
 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 中各种需要执行的初始化工作。


清单 2. StartupCommand.java 的源码
				
 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 类。


清单 3. LoginScreenMediator.java 的源码
				
 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 的源码。


清单 4. LoginCommand.java 的源码
				
 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 事件,它关注业务流程,所以也是一个非常有力帮助需求人员和设计人员之间的沟通。

猜你喜欢

转载自blog.csdn.net/jackson_0606/article/details/8112239