技术开发 频道

采用敏捷方法进行用户界面开发

    现在,我们已经建立了登录功能,并用单元测试覆盖了他,但没有可视的GUI来显示它。那下一步该做什么呢?对于已经作的和测试的实际功能,在GUI方面做的是创建和显示图像元素,然后在适当的时候调用login()方法。这个功能是普通和容易建立的,所以他不包含能中断和需要单元测试的复杂行为。因此,当建立GUI元素时,我们不需要去做测试先行的开发。例5展示了创建对话框窗口的Swing类LoginDialogView ,他的实现在LoginDialogView.java.文件。

    LoginDialogView.java

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
      public class LoginDialogView extends JFrame implements ActionListener {
      protected JTextField usernameField;
  protected JTextField passwordField;
      protected JButton loginButton;
      protected JButton cancelButton;
       private LoginDialog dialog;
       LoginDialogView(LoginDialog dlg) {
        super("Login");
        setSize(300, 140);
        dialog = dlg;
        addControls();
       loginButton.addActionListener( this );
       cancelButton.addActionListener( this );
    }
     public void actionPerformed(ActionEvent e) {
       String cmd = e.getActionCommand();
       if (cmd.equals("Login") && dialog.login(usernameField.getText(),     passwordField.getText())) { hide(); } }
     private void addControls() {
       Container contentPane = this.getContentPane();
       contentPane.setLayout(new GridBagLayout());
       GridBagConstraints c = new GridBagConstraints();
       JLabel label1 = new JLabel("Username:", Label.RIGHT);
       c.insets = new Insets(2, 2, 2, 2); c.gridx = 0;
       c.gridy = 0; contentPane.add(label1, c);
       usernameField = new JTextField("", 60);
       usernameField.setMinimumSize(new Dimension(180, 30));
       c.gridx = 1;
       contentPane.add(usernameField, c);
       JLabel label2 = new JLabel("Password:", Label.RIGHT);
       c.gridx = 0;
       c.gridy = 1;
       contentPane.add(label2, c);
       passwordField = new JTextField("", 60);
       passwordField.setMinimumSize(new Dimension(180, 30));
       c.gridx = 1;
       contentPane.add(passwordField, c);
       loginButton = new JButton("Login");
       c.gridx = 0;
       c.gridy = 2;
       contentPane.add(loginButton, c);
       cancelButton = new JButton("Cancel");
       c.gridx = 1;
       contentPane.add(cancelButton, c); }}

    LoginDialogView 包含了文本域,标签,和按钮元素。除了普通的GUI行为外,他只是有一个简单的行为,被actionPerformed() 方法实现。这个行为就是当登录按钮被点击后,login()方法被调用。如果登录成功,对话框就被所调用的hide()方法所关闭。

    为了调用login()函数,在LoginDialogView 构造器里需要接收一个LoginDialog实例。另外,他组装了完整的GUI设置和事件处理代码。大部分代码在addControls() 里,他简单的创建和排版了窗体上的GUI元素。

    LoginDialogView 代码示范了一个GUI瘦视图元素怎样被设计使它只包含普通的GUI代码,而把重要的需要测试应用的行为放到一个单独,可测试的敏捷对象中。
    LoginDialogView 只需要通过创建它来测试,察看他,从用户的角度确认它看起来和运行起来象期望的那样。例6展示了可执行的类APPMain,它创建了对话窗体来传递可用性测试(指的是传递loginDialog的实例)。

    AppMain.java

    public class AppMain {
     public static void main(String[] args)
     { AppMain app = new AppMain();
     }
     public AppMain() {
     LoginDialog dialog = new LoginDialog();
     LoginDialogView view = new LoginDialogView(dialog);
     view.show();
      while (view.isVisible()) {
      try { Thread.currentThread().sleep(100);
      }
      catch(Exception x) {}
      }
      System.exit(0);
     }
    }

    AppMain 类简单的创建一个LoginDialog 和LoginDialogView ,显示视图,休眠直到视图关闭,然后退出。
    AppMain 象下面一样运行

    java –classpath "." AppMain

    运行它创建登录对话框,如图4所示

    

 
     Figure 4. The login dialog window
    和登录对话框交互验证了用图4所示的值登录,会登录成功然后窗体关闭。试着用其它的值登录,窗体将保持打开,因为登录失败了。取消按钮关闭窗体,就象窗体的关闭按钮一样。这个登录对话框就如同设计的那样运行。

    解决方案

    我们已经根据TDD创建了登录对话框和一个敏捷对象/瘦视图设计模式。得到了一个有很好构架和功能的程序。有功能的应用行为被单元测试所覆盖,普通的用来显示的代码不需要复杂的GUI测试。图5展示了我们所开发的这个软件的构架。

        

    Figure 5. The classes LoginDialog, LoginDialogView, and LoginDialogTest
    基于此,其他的特性可以被加入。登录对话框可以有一个消息域去提醒用户登录失败。其他的登陆参数域也可以被加入。一个单独的验证对象可以被创建,硬编码的登录值可以被删掉。不管怎么变化,TDD和敏捷对象/瘦视图模式提供了一个设计和实现上的清晰的方向。重要的应用功能是在于可以测试的敏捷的对象,和在瘦视图中普通的显示用的代码的。

    要找更多的测试驱动GUI开发的详细例子,JUnit和其他xUnit测试框架的广泛覆盖,TDD,还有单元测试策略,请看我的书Unit Test Frameworks ,2004年11月O'Reilly出版。

0
相关文章