当前页面位置: » 丰搜网 » 文档中心 » 详细内容
[关注敏捷]采用敏捷方法进行用户界面开发
用ajax来控制书签和回退按钮作者:paul hamill译者:boool版权声明:任何获得matrix授权的网站,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明作者:paul hamill;boool
原文地址:http://www.on
java.com/pub/a/on
java/2004/11/17/agileuser_1.
html中文地址:http://www.matrix.org.cn/resource/article/44/44038_agile+interface+
gui.
html关键词: agile;interface;
gui摘要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, j
webunit和类似的工具
测试。许多
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.javapublic class logindialog {
logindialog() {}
public boolean login(string username, string password) {
return true;
}
}使用下面的命令来运行代码
javac -classpath ".;junit.jar" logindialogtest.java
javac -classpath "." logindialog.javaclasspath 必须包括junit.jar 来运行单元
测试,因为他使用了junit.在
linux,mac osx,还有其他的
unix系统上,classpath将包含一个冒号(:)而不是想下面那样用一个分号。
测试将如下运行
java -classpath ".;junit.jar" junit.textui.testrunner logindialogtest单元
测试通过了,真好!不幸的是,这个编码只是模拟一下。login()方法将总是批准登录。毋庸置疑,客户将不会欣赏这种水平的
安全机制。显然,要写的下一个
测试是验证如果给的条件不正确的话将失败。例3展示了logindialogtest 的第二个
测试方法去实现这个目的,testloginfail() 。既然两个
测试都使用一个logindialog 的实例,
测试类被重构为在他的setup() 方法里创建一个固定的
测试用的logindialog。
logindialogtest.javaimport 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.javapublic 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.javaimport 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.javapublic 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出版。
资源·这是一个敏捷的时代?:http://www.matrix.org.cn/resource/news/314_agile.
html ·matrix-
java开发者社区:http://www.matrix.org.cn
·on
java.com:on
java.com
paul hamill 是一个富有经验的软件开发者,有十年以上的c/c++,
java和其他语言的编码开发经验。他最近的著作是unit test frameworks.