后来我又想,界面固然变化很少了,但界面内容需要调用的后台API却是未曾定型,所以如果在TDD时从界面写起也许更合适,因为界面本身和测试用例一样,也算是后台API的一个用户。
在写SWT的界面很烦的是无法应用TDD,甚至无法写自动单元测试代码。单元测试在界面开发中显得特别无力,在<<JUnit in Action>>这本书中虽然说到JSP的测试,但Swing的自动测试没有提及,更不要说SWT了。我曾多方寻找SWT的自动测试框架,在eclips.org上有eclipse-test-framework,不过使用很复杂,至今还没怎么搞懂。虽然界面测试很困难,但我们还是可以通过一些小技术来加速界面开发,和进行半自动测试的。
不过本篇且不是讲SWT界面的测试的,这里只是临时记下自己的一些想法。这篇主要讲的抽取公共类。
二、抽取公共类类
TDD有一个基本思想:拒绝代码的复制/粘帖。也就是说一段相同的代码,在项目中应该只存在一处。同理,从更高处来说,几个项目中常用的类也应该只存在于一处。其实,我们平时编程就已经发现很多类和代码是通用的,不过我们依然习惯于去老项目中翻看代码,然后复制粘贴于新项目中来。这样的做法是违反TDD“拒绝代码的复制/粘帖”原则的,所以在平时我们就应该注意提炼自己的公共代码库,说不定几年后我们就能形成自己的一个框架,很多框架和类库不就是这样形成的吗,比如 appache 的commons系列,比如Struts、比如Spring。
今天我抽取的公共类是一个图标闪烁类,和一个时间显示Label。如下图,图中上部左边的“邮件图标”和右边的“时间显示”,可以抽取出来做成公共类,在各种SWT项目中使用。特别是邮件图标的闪烁显示,因为SWT还不支持动态GIF,所以只能用多线程轮换图片的方式来实现图标闪烁,我想这样的类提取出来,用作还是挺大的。
三、FlashImage类
这里没有让FlashImage 继承自Label,而是内嵌了一个Label。这是应用于组合优先于继承的原则,并且Label是不可继承的,虽然SWT中并没有把它定义成Final ,但却会在其内部做一个子类检查,如果是继承自Label则会报出异常。和Label一样的还是Shell,虽然没有final 修饰符,但也是不可继承的。
这里还涉及SWT的多线程编程,在停止线程时不能用Thead#stop方法的,这个方法已经禁用了。另外,线程要在label#dispose后也关闪掉,所以在label加了一个disponse的事件监听。
package
@H_404_10@cn.com.chengang.myswt;
import @H_404_10@org.eclipse.core.runtime.Assert;
import @H_404_10@org.eclipse.swt.events.DisposeEvent;
import @H_404_10@org.eclipse.swt.events.DisposeListener;
import @H_404_10@org.eclipse.swt.events.MouseListener;
import @H_404_10@org.eclipse.swt.graphics.Image;
import @H_404_10@org.eclipse.swt.widgets.Composite;
import @H_404_10@org.eclipse.swt.widgets.Display;
import @H_404_10@org.eclipse.swt.widgets.Label;
import @H_404_10@cn.com.chengang.common.util.CommonUtil;
public @H_404_10@ class @H_404_10@FlashImage{
private @H_404_10@LabelflashLabel;
private @H_404_10@ImagestopImage;
private @H_404_10@Image[]flashImages;
private @H_404_10@ long @H_404_10@flashSpaceTime @H_404_10@= @H_404_10@ @H_404_10@100 @H_404_10@; // 闪动间隔时间
@H_404_10@
private @H_404_10@ boolean @H_404_10@progressStop @H_404_10@= @H_404_10@ true @H_404_10@; // 处理是否停止的标志
@H_404_10@
/**
* @param comp
* @param style与Label的style相同
* @param flashImages闪动的图像
* @param stopImage停止时的图像
*/ @H_404_10@
public @H_404_10@FlashImage(Compositecomp, int @H_404_10@style,Image[]flashImages,ImagestopImage){
Assert.isTrue(flashImages @H_404_10@!= @H_404_10@ null @H_404_10@ @H_404_10@&& @H_404_10@flashImages.length @H_404_10@!= @H_404_10@ @H_404_10@0 @H_404_10@);
flashLabel @H_404_10@= @H_404_10@ new @H_404_10@Label(comp,style);
this @H_404_10@.flashImages @H_404_10@= @H_404_10@flashImages;
this @H_404_10@.stopImage @H_404_10@= @H_404_10@stopImage;
flashLabel.setImage(stopImage);
// 监听Dispose事件,在其销毁时停掉线程
@H_404_10@flashLabel.addDisposeListener( new @H_404_10@DisposeListener(){
public @H_404_10@ void @H_404_10@widgetDisposed(DisposeEvente){
progressStop @H_404_10@= @H_404_10@ true @H_404_10@;
}
});
}
/**
*开始闪动
*/ @H_404_10@
public @H_404_10@ void @H_404_10@flash(){
if @H_404_10@( @H_404_10@! @H_404_10@progressStop)
return @H_404_10@;
progressStop @H_404_10@= @H_404_10@ false @H_404_10@;
new @H_404_10@ImageFlashThread().start();
}
/**
*停止闪动
*/ @H_404_10@
public @H_404_10@ void @H_404_10@flashStop(){
progressStop @H_404_10@= @H_404_10@ true @H_404_10@;
}
/**
*设置闪动间隔的时间
* @param time默认100(100毫秒)
*/ @H_404_10@
public @H_404_10@ void @H_404_10@setFlashSpaceTime( long @H_404_10@time){
this @H_404_10@.flashSpaceTime @H_404_10@= @H_404_10@time;
}
private @H_404_10@DisplaygetDisplay(){
return @H_404_10@Display.getDefault();
}
public @H_404_10@ void @H_404_10@addMouseListener(MouseListenerlistener){
flashLabel.addMouseListener(listener);
}
/**
*闪动图像线程
*/ @H_404_10@
private @H_404_10@ class @H_404_10@ImageFlashThread extends @H_404_10@Thread{
public @H_404_10@ void @H_404_10@run(){
int @H_404_10@i @H_404_10@= @H_404_10@ @H_404_10@0 @H_404_10@;
while @H_404_10@( @H_404_10@! @H_404_10@progressStop){
if @H_404_10@(i @H_404_10@== @H_404_10@flashImages.length) // 到头循环
@H_404_10@i @H_404_10@= @H_404_10@ @H_404_10@0 @H_404_10@;
setCurrentImage(flashImages[i]);
CommonUtil.sleep(flashSpaceTime); // 闪动间隔
@H_404_10@i @H_404_10@++ @H_404_10@;
}
setCurrentImage(stopImage);
}
private @H_404_10@ void @H_404_10@setCurrentImage( final @H_404_10@Imageimage){
getDisplay().asyncExec( new @H_404_10@Runnable(){
public @H_404_10@ void @H_404_10@run(){
flashLabel.setImage(image);
}
});
}
}
}
import @H_404_10@org.eclipse.core.runtime.Assert;
import @H_404_10@org.eclipse.swt.events.DisposeEvent;
import @H_404_10@org.eclipse.swt.events.DisposeListener;
import @H_404_10@org.eclipse.swt.events.MouseListener;
import @H_404_10@org.eclipse.swt.graphics.Image;
import @H_404_10@org.eclipse.swt.widgets.Composite;
import @H_404_10@org.eclipse.swt.widgets.Display;
import @H_404_10@org.eclipse.swt.widgets.Label;
import @H_404_10@cn.com.chengang.common.util.CommonUtil;
public @H_404_10@ class @H_404_10@FlashImage{
private @H_404_10@LabelflashLabel;
private @H_404_10@ImagestopImage;
private @H_404_10@Image[]flashImages;
private @H_404_10@ long @H_404_10@flashSpaceTime @H_404_10@= @H_404_10@ @H_404_10@100 @H_404_10@; // 闪动间隔时间
@H_404_10@
private @H_404_10@ boolean @H_404_10@progressStop @H_404_10@= @H_404_10@ true @H_404_10@; // 处理是否停止的标志
@H_404_10@
/**
* @param comp
* @param style与Label的style相同
* @param flashImages闪动的图像
* @param stopImage停止时的图像
*/ @H_404_10@
public @H_404_10@FlashImage(Compositecomp, int @H_404_10@style,Image[]flashImages,ImagestopImage){
Assert.isTrue(flashImages @H_404_10@!= @H_404_10@ null @H_404_10@ @H_404_10@&& @H_404_10@flashImages.length @H_404_10@!= @H_404_10@ @H_404_10@0 @H_404_10@);
flashLabel @H_404_10@= @H_404_10@ new @H_404_10@Label(comp,style);
this @H_404_10@.flashImages @H_404_10@= @H_404_10@flashImages;
this @H_404_10@.stopImage @H_404_10@= @H_404_10@stopImage;
flashLabel.setImage(stopImage);
// 监听Dispose事件,在其销毁时停掉线程
@H_404_10@flashLabel.addDisposeListener( new @H_404_10@DisposeListener(){
public @H_404_10@ void @H_404_10@widgetDisposed(DisposeEvente){
progressStop @H_404_10@= @H_404_10@ true @H_404_10@;
}
});
}
/**
*开始闪动
*/ @H_404_10@
public @H_404_10@ void @H_404_10@flash(){
if @H_404_10@( @H_404_10@! @H_404_10@progressStop)
return @H_404_10@;
progressStop @H_404_10@= @H_404_10@ false @H_404_10@;
new @H_404_10@ImageFlashThread().start();
}
/**
*停止闪动
*/ @H_404_10@
public @H_404_10@ void @H_404_10@flashStop(){
progressStop @H_404_10@= @H_404_10@ true @H_404_10@;
}
/**
*设置闪动间隔的时间
* @param time默认100(100毫秒)
*/ @H_404_10@
public @H_404_10@ void @H_404_10@setFlashSpaceTime( long @H_404_10@time){
this @H_404_10@.flashSpaceTime @H_404_10@= @H_404_10@time;
}
private @H_404_10@DisplaygetDisplay(){
return @H_404_10@Display.getDefault();
}
public @H_404_10@ void @H_404_10@addMouseListener(MouseListenerlistener){
flashLabel.addMouseListener(listener);
}
/**
*闪动图像线程
*/ @H_404_10@
private @H_404_10@ class @H_404_10@ImageFlashThread extends @H_404_10@Thread{
public @H_404_10@ void @H_404_10@run(){
int @H_404_10@i @H_404_10@= @H_404_10@ @H_404_10@0 @H_404_10@;
while @H_404_10@( @H_404_10@! @H_404_10@progressStop){
if @H_404_10@(i @H_404_10@== @H_404_10@flashImages.length) // 到头循环
@H_404_10@i @H_404_10@= @H_404_10@ @H_404_10@0 @H_404_10@;
setCurrentImage(flashImages[i]);
CommonUtil.sleep(flashSpaceTime); // 闪动间隔
@H_404_10@i @H_404_10@++ @H_404_10@;
}
setCurrentImage(stopImage);
}
private @H_404_10@ void @H_404_10@setCurrentImage( final @H_404_10@Imageimage){
getDisplay().asyncExec( new @H_404_10@Runnable(){
public @H_404_10@ void @H_404_10@run(){
flashLabel.setImage(image);
}
});
}
}
}
给出一个客户端使用的示例:
@H_404_10@Image[]flashs
@H_404_10@=
@H_404_10@
new
@H_404_10@Image[]{
ImagesContext.getImage(ImagesContext.MAIL),//ImagesContext是我自己写的一个管理Image的类
ImagesContext.getImage(ImagesContext.MAIL_GRAY)};
ImagestopImage @H_404_10@= @H_404_10@ImagesContext.getImage(ImagesContext.MAIL);
flashImage @H_404_10@= @H_404_10@ new @H_404_10@FlashImage(c,SWT.NONE,flashs,stopImage);
flashImage.setFlashSpaceTime( @H_404_10@800 @H_404_10@);
flashImage.addMouseListener( new @H_404_10@MouseAdapter(){
public @H_404_10@ void @H_404_10@mouseDown(MouseEvente){
//do something......
}
});
ImagesContext.getImage(ImagesContext.MAIL),//ImagesContext是我自己写的一个管理Image的类
ImagesContext.getImage(ImagesContext.MAIL_GRAY)};
ImagestopImage @H_404_10@= @H_404_10@ImagesContext.getImage(ImagesContext.MAIL);
flashImage @H_404_10@= @H_404_10@ new @H_404_10@FlashImage(c,SWT.NONE,flashs,stopImage);
flashImage.setFlashSpaceTime( @H_404_10@800 @H_404_10@);
flashImage.addMouseListener( new @H_404_10@MouseAdapter(){
public @H_404_10@ void @H_404_10@mouseDown(MouseEvente){
//do something......
}
});
四、TimeLabel类
时间显示Label则和FlashImage类似,可以说是它的一个简化版
package
@H_404_10@cn.com.chengang.myswt;
import @H_404_10@java.text.DateFormat;
import @H_404_10@java.text.SimpleDateFormat;
import @H_404_10@java.util.Date;
import @H_404_10@org.eclipse.swt.events.DisposeEvent;
import @H_404_10@org.eclipse.swt.events.DisposeListener;
import @H_404_10@org.eclipse.swt.layout.GridData;
import @H_404_10@org.eclipse.swt.widgets.Composite;
import @H_404_10@org.eclipse.swt.widgets.Display;
import @H_404_10@org.eclipse.swt.widgets.Label;
import @H_404_10@cn.com.chengang.common.util.CommonUtil;
/**
* @author chengang2006-4-19
*/ @H_404_10@
public @H_404_10@ class @H_404_10@TimeLabel{
private @H_404_10@LabeltimeLabel;
private @H_404_10@ long @H_404_10@flashSpaceTime @H_404_10@= @H_404_10@ @H_404_10@480 @H_404_10@; // 闪动间隔时间
@H_404_10@ private @H_404_10@ boolean @H_404_10@progressStop @H_404_10@= @H_404_10@ false @H_404_10@; // 处理是否停止的标志
@H_404_10@ private @H_404_10@DateFormatdateFormat @H_404_10@= @H_404_10@ new @H_404_10@SimpleDateFormat( @H_404_10@" @H_404_10@HH:mm:ss @H_404_10@" @H_404_10@);
public @H_404_10@TimeLabel(Compositecomp, int @H_404_10@style){
timeLabel @H_404_10@= @H_404_10@ new @H_404_10@Label(comp,style);
timeLabel.setText( @H_404_10@" @H_404_10@sssssssssssssssssss @H_404_10@" @H_404_10@);
// 监听Dispose事件,在其销毁时停掉线程
@H_404_10@timeLabel.addDisposeListener( new @H_404_10@DisposeListener(){
public @H_404_10@ void @H_404_10@widgetDisposed(DisposeEvente){
progressStop @H_404_10@= true @H_404_10@;
}
});
new @H_404_10@ShowTimeThread().start();
}
public @H_404_10@ void @H_404_10@dispose(){
progressStop @H_404_10@= @H_404_10@ true @H_404_10@;
}
private @H_404_10@DisplaygetDisplay(){
return @H_404_10@Display.getDefault();
}
private @H_404_10@ class @H_404_10@ShowTimeThread extends @H_404_10@Thread{
public @H_404_10@ void @H_404_10@run(){
while @H_404_10@( @H_404_10@! @H_404_10@progressStop){
Stringstr @H_404_10@= @H_404_10@dateFormat.format( new @H_404_10@Date());
setText(str);
CommonUtil.sleep(flashSpaceTime); // 闪动间隔
//CommonUtil是我自己写的一个常用工具方法类,sleep的代码如下
//public class CommonUtil {
// public static void sleep(long millis) {
// try {
// Thread.sleep(millis);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
@H_404_10@}
}
private @H_404_10@ void @H_404_10@setText( final @H_404_10@Stringtime){
getDisplay().asyncExec( new @H_404_10@Runnable(){
public @H_404_10@ void @H_404_10@run(){
timeLabel.setText(time);
}
});
}
}
public @H_404_10@ void @H_404_10@setLayoutData(GridDatadata){
timeLabel.setLayoutData(data);
}
public @H_404_10@ void @H_404_10@setDateFormate(DateFormatformat){
dateFormat @H_404_10@= @H_404_10@format;
}
}
import @H_404_10@java.text.DateFormat;
import @H_404_10@java.text.SimpleDateFormat;
import @H_404_10@java.util.Date;
import @H_404_10@org.eclipse.swt.events.DisposeEvent;
import @H_404_10@org.eclipse.swt.events.DisposeListener;
import @H_404_10@org.eclipse.swt.layout.GridData;
import @H_404_10@org.eclipse.swt.widgets.Composite;
import @H_404_10@org.eclipse.swt.widgets.Display;
import @H_404_10@org.eclipse.swt.widgets.Label;
import @H_404_10@cn.com.chengang.common.util.CommonUtil;
/**
* @author chengang2006-4-19
*/ @H_404_10@
public @H_404_10@ class @H_404_10@TimeLabel{
private @H_404_10@LabeltimeLabel;
private @H_404_10@ long @H_404_10@flashSpaceTime @H_404_10@= @H_404_10@ @H_404_10@480 @H_404_10@; // 闪动间隔时间
@H_404_10@ private @H_404_10@ boolean @H_404_10@progressStop @H_404_10@= @H_404_10@ false @H_404_10@; // 处理是否停止的标志
@H_404_10@ private @H_404_10@DateFormatdateFormat @H_404_10@= @H_404_10@ new @H_404_10@SimpleDateFormat( @H_404_10@" @H_404_10@HH:mm:ss @H_404_10@" @H_404_10@);
public @H_404_10@TimeLabel(Compositecomp, int @H_404_10@style){
timeLabel @H_404_10@= @H_404_10@ new @H_404_10@Label(comp,style);
timeLabel.setText( @H_404_10@" @H_404_10@sssssssssssssssssss @H_404_10@" @H_404_10@);
// 监听Dispose事件,在其销毁时停掉线程
@H_404_10@timeLabel.addDisposeListener( new @H_404_10@DisposeListener(){
public @H_404_10@ void @H_404_10@widgetDisposed(DisposeEvente){
progressStop @H_404_10@= true @H_404_10@;
}
});
new @H_404_10@ShowTimeThread().start();
}
public @H_404_10@ void @H_404_10@dispose(){
progressStop @H_404_10@= @H_404_10@ true @H_404_10@;
}
private @H_404_10@DisplaygetDisplay(){
return @H_404_10@Display.getDefault();
}
private @H_404_10@ class @H_404_10@ShowTimeThread extends @H_404_10@Thread{
public @H_404_10@ void @H_404_10@run(){
while @H_404_10@( @H_404_10@! @H_404_10@progressStop){
Stringstr @H_404_10@= @H_404_10@dateFormat.format( new @H_404_10@Date());
setText(str);
CommonUtil.sleep(flashSpaceTime); // 闪动间隔
//CommonUtil是我自己写的一个常用工具方法类,sleep的代码如下
//public class CommonUtil {
// public static void sleep(long millis) {
// try {
// Thread.sleep(millis);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
@H_404_10@}
}
private @H_404_10@ void @H_404_10@setText( final @H_404_10@Stringtime){
getDisplay().asyncExec( new @H_404_10@Runnable(){
public @H_404_10@ void @H_404_10@run(){
timeLabel.setText(time);
}
});
}
}
public @H_404_10@ void @H_404_10@setLayoutData(GridDatadata){
timeLabel.setLayoutData(data);
}
public @H_404_10@ void @H_404_10@setDateFormate(DateFormatformat){
dateFormat @H_404_10@= @H_404_10@format;
}
}
客户端的使用代码示例:
@H_404_10@TimeLabeltimeLabel
@H_404_10@=
@H_404_10@
new
@H_404_10@TimeLabel(topComp,SWT.NONE);
// timeLabel.setDateFormate(newSimpleDateFormat("HH:mm:ss"));
@H_404_10@timeLabel.setLayoutData( new @H_404_10@GridData(GridData.END,GridData.CENTER, true @H_404_10@, true @H_404_10@));
// timeLabel.setDateFormate(newSimpleDateFormat("HH:mm:ss"));
@H_404_10@timeLabel.setLayoutData( new @H_404_10@GridData(GridData.END,GridData.CENTER, true @H_404_10@, true @H_404_10@));
作者简介
陈刚,广西桂林人,著作有《Eclipse从入门到精通》
您可以通过其博客了解更多信息和文章:http://www.ChenGang.com.cn版权声明:本博客所有文章仅适用于非商业性转载,并请在转载时注明出处及作者的署名