分享
 
 
 

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

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

摘要

1991年秋,在美国勒海大学亚科卡学院的一份研究报告《21世纪美国制造业的战略:一个工业主导的观点》中,首次提出了灵敏竞争的概念.而今天,我们似乎已经看到,灵敏已经在我们身边,形影不离.

之前Matrix一则讨论激烈的新闻(http://www.matrix.org.cn/resource/news/314_Agile.Html), 也表达了灵敏在今天的热度.下面通过一个简单的例子, 告诉你如何起步灵敏开发..

概述

假如你没有采用灵敏的方式,那你就落后了。这是最近SD Best PRactices 2004会议上的标语。灵敏的方法就象XP和Scrum正在世界的软件发展中,变得越来越普遍了。灵敏是一个巨大的改变,让软件开发者把重心转移到质量和速度上。这对已经被比喻成面向对象设计的软件开发有很大的影响。但是,这种影响的一些方面已经放慢了:GUI的开发,自从大多数软件包含了一些类型的GUI,而且比较多的软件开发的百分比是去完成以GUI为中心的,应用灵敏的优势去开发GUI就是要害问题了。

什么在阻止人们用灵敏的方法来开发GUI呢?不论他们的应用是基于web的或是桌面应用,大多数开发者不做用户界面user interface的测试驱动开发test-driven development (TDD)。这都因为一个简单的原因:单元测试GUI是很困难的。测试GUI是很乏味沉闷而且轻易出错的,包含了模拟用户事件的复杂代码,在事件传播和控制重绘的时候等待,然后在他显示给用户之前,尝试着检测状态。灵敏依靠于测试驱动开发,但是为GUI的特定行为来写有效的测试代码是困难的。在cube farm(办公农庄,用隔断间隔成很多小工作间的办公室? 商务英语)的GUI方面上,质量和设计从灵敏方法中受益已经被完全熟悉到。

灵敏实践正在渗透进这个领域。单元测试GUI元素的工具激增,JFCUnit 框架测试GUI是用java Swing,基于Web的GUI能被HTMLUnit, HTTPUnit, jWebUnit和类似的工具测试。许多GUI构造器和工具包和单元测试工具有关系,比如VBUnit是为了Visual Basic,QtUnit是给Qt用的。

工具已经存在了,但是处理仍是不确定的。在测试驱动开发(TDD)中,每个代码的改变都在新行为的单元测试前。在开发GUI时,许多变化不过是视觉显示上的调整,比如改变元素的位置,文本,或者颜色。你可以加一个按钮,建一个菜单项,或者构造一个对话框。但是怎样和为什么你要测试这种变化呢?测试每个标签或者颜色值是很愚蠢的。同样的,对于标准的元素象按钮和域,测试他们通常的行为是没有意义的,象对鼠标移动的响应,键的按下,点击,和诸如此类的。这些事情是不可能被中断的。怎样去测试他们的问题只是徒劳的增添了做GUI测试的难度。

一个要害的问题:怎样做测试先行的开发?答案就在于GUI的编码是怎样组织的。灵敏方式的领袖例如Kent Beck 和 David Astels建议在构造GUI的时候要保持视图对象尽可能是轻量的,而且“在表面下”( below the surface.)测试视图层。这个 灵敏对象/瘦视图 模型和我们熟悉的 文档-视图 及 客户端-服务器模式类似,但是被应用于个别的GUI元素。内容和表现的分离改善了代码的设计,使他更模块化和更利于测试。每个用户界面的组件被实现为一个灵敏对象,包括将要被测试的应用的行为,但不包括GUI表现的代码。每个灵敏对象有一个相应的瘦视图类只包括普通的GUI行为。采用这种设计模式,GUI构造变得可以被应用于 测试驱动开发(TDD) 处理了。

例子:构造一个登录对话框

让我们进入一个例子看看怎样使用TDD和 灵敏对象/瘦视图 代码设计模式去开发一个GUI对话框。起初,让我们考虑对话框的图形,灵敏开发提倡预先最小化设计,让软件构架在多次循环开发中重构,但是这个方法对GUI设计不是很合适。设计一个用户界面是一个创造的过程,应该规范地处理,画草图,做原型,和可用性测试。然后,尽管在GUI下的代码可以用TDD迭代地设计,一个形象的设计草图是明智的第一步。这个对话框的基本的设计在图1中勾画出来。

Figure 1. GUI design sketch for login dialog

这个对话框很简单,包括用户名和密码域,相应的静态文本框和标签,登录和取消按钮。做为一个他行为的初始轮廓,我们决定登录成功的话对话框关闭,登录失败的话对话框仍然开着。取消按钮也关闭对话框。

基本的 灵敏对象/瘦视图 代码类设计的对话框实现在图2中表示。

Figure 2. The classes LoginDialog and LoginDialogView

灵敏对象类LoginDialog 将包含一个方法对应对话框的每个功能行为。瘦视图类LoginDialogView 将只包含简单的和显示相关的代码,还有get/set 方法去读取和设置显示的信息。在这个过程里,只有LoginDialog里复杂的功能需要被单元测试。我们可以十分自信在LoginDialogView 里的简单行为可以正常工作。

第一个构造的组件是灵敏对象LoginDialog 。他需要一个相应的测试类LoginDialogTest 。第一个测试方法将要验证登录方法,如图3所示。

Figure 3. The smart object LoginDialog and its test class LoginDialogTest

作为测试先行的开发方法规定,首先要写单元测试。测试预期和定义了要被测试的功能设计。我们需要获得一个用户名和密码,然后返回一个登录成功或者登录失败。一个用来判定的接口方法来做刚才所述的

boolean login(String username, String passWord);

测试类LoginDialogTest 将测试这个功能。例1展示了在LoginDialogTest.java. 文件中他的初始实现。

LoginDialogTest.java

import junit.framework.*;

public class LoginDialogTest extends TestCase {

public void testLogin() {

LoginDialog dialog = new LoginDialog();

assertTrue( dialog.login("user", "passwd") );

}}

这个测试是基于JUnit基础测试类TestCase的。测试方法testLogin()创建了一个LoginDialog 的实例,调用了他的login()方法,然后判定结果是真。这段代码将不会编译,因为LoginDialog 不存在。在TDD过程后,LoginDialog

将生成和保存,代码编译后,测试运行验证将象预期的那样失败(因为方法没有实现)。然后 LoginDialog 为了通过单元测试给出最小的实现,遵照灵敏的圣条 做 可能工作的最简单的事情(the simplest thing that could possibly work)。例2展示了最初的LoginDialog 版本,用最少的代码通过了单元测试,实现在LoginDialog.java. 文件里。

LoginDialog.java

public class LoginDialog {

LoginDialog() {}

public boolean login(String username, String password) {

return true; } }

使用下面的命令来运行代码

javac -classpath ".;junit.jar" LoginDialogTest.java

javac -classpath "." LoginDialog.java

classpath 必须包括junit.jar 来运行单元测试,因为他使用了JUnit.在linux,Mac OSX,还有其他的UNIX系统上,classpath将包含一个冒号(:)而不是想下面那样用一个分号。

测试将如下运行

java -classpath ".;junit.jar" junit.textui.TestRunner LoginDialogTest

单元测试通过了,真好!不幸的是,这个编码只是模拟一下。Login()方法将总是批准登录。毋庸置疑,客户将不会欣赏这种水平的安全机制。显然,要写的下一个测试是验证假如给的条件不正确的话将失败。例3展示了LoginDialogTest 的第二个测试方法去实现这个目的,testLoginFail() 。既然两个测试都使用一个LoginDialog 的实例,测试类被重构为在他的setUp() 方法里创建一个固定的测试用的LoginDialog。

LoginDialogTest.java

import junit.framework.*;

public class LoginDialogTest extends TestCase {

private LoginDialog dialog;

public void setUp() {

dialog = new LoginDialog();

}

public void testLogin() {

assertTrue( dialog.login("user", "passwd") );

}

public void testLoginFail() {

assertFalse( dialog.login("", "") );

}}

LoginDialog必须得通过新的测试,不能在第一次测试的时候有失败。TDD过程引导我们构造我们需要的真正的功能,在用正确的用户名和密码登录的时候,能成功登录,假如不是,就失败。例4展示了按此修改的LoginDialog

LoginDialog.java

public class LoginDialog {

private String user = "user";

private String passwd = "passwd";

LoginDialog() {}

public boolean login(String username, String password) {

if (user.equals(username) && passwd.equals(password))

return true;

else

return false;

} }

LoginDialog现在能通过所有的测试。为此,他包括了符合成功登录条件的用户名和密码域。显然,这只是比第一个版本的安全性能稍微好一些。登录代码不应该包含认证的硬编码!基于这点,我们应该引入一个单独的类来包含LoginDialog 用的验证用户的登录信息。然而,这个例子是关于GUI构造的,那让我们暂停这个不安全的登录代码,继续GUI方面。

现在,我们已经建立了登录功能,并用单元测试覆盖了他,但没有可视的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和灵敏对象/瘦视图模式提供了一个设计和实现上的清楚的方向。重要的应用功能是在于可以测试的灵敏的对象,和在瘦视图中普通的显示用的代码的。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有