[TDD开发的全过程] 三、抽取公共类
发布时间:2020-12-13 20:37:31 所属栏目:百科 来源:网络整理
导读:文/陈刚 at 2006年4月19日 from http://www.chengang.com.cn 一、前言 在前面一章中,我有这样的想法。 在这里我面临一个选择,是先写界面呢?还是先写底层API?我想,先写界面很难写测试代码,而且界面在设计时已经定下来了,基本不会变了 后来我又想,界面
文/陈刚 at 2006年4月19日 from
http://www.chengang.com.cn
一、前言 在前面一章中,我有这样的想法。
在这里我面临一个选择,是先写界面呢?还是先写底层API?我想,先写界面很难写测试代码,而且界面在设计时已经定下来了,基本不会变了
后来我又想,界面固然变化很少了,但界面内容需要调用的后台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
cn.com.chengang.myswt;
import org.eclipse.core.runtime.Assert; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import cn.com.chengang.common.util.CommonUtil; public class FlashImage{ private LabelflashLabel; private ImagestopImage; private Image[]flashImages; private long flashSpaceTime = 100 ; // 闪动间隔时间 private boolean progressStop = true ; // 处理是否停止的标志 /** * @param comp * @param style与Label的style相同 * @param flashImages闪动的图像 * @param stopImage停止时的图像 */ public FlashImage(Compositecomp, int style,Image[]flashImages,ImagestopImage){ Assert.isTrue(flashImages != null && flashImages.length != 0 ); flashLabel = new Label(comp,style); this .flashImages = flashImages; this .stopImage = stopImage; flashLabel.setImage(stopImage); // 监听Dispose事件,在其销毁时停掉线程 flashLabel.addDisposeListener( new DisposeListener(){ public void widgetDisposed(DisposeEvente){ progressStop = true ; } }); } /** *开始闪动 */ public void flash(){ if ( ! progressStop) return ; progressStop = false ; new ImageFlashThread().start(); } /** *停止闪动 */ public void flashStop(){ progressStop = true ; } /** *设置闪动间隔的时间 * @param time默认100(100毫秒) */ public void setFlashSpaceTime( long time){ this .flashSpaceTime = time; } private DisplaygetDisplay(){ return Display.getDefault(); } public void addMouseListener(MouseListenerlistener){ flashLabel.addMouseListener(listener); } /** *闪动图像线程 */ private class ImageFlashThread extends Thread{ public void run(){ int i = 0 ; while ( ! progressStop){ if (i == flashImages.length) // 到头循环 i = 0 ; setCurrentImage(flashImages[i]); CommonUtil.sleep(flashSpaceTime); // 闪动间隔 i ++ ; } setCurrentImage(stopImage); } private void setCurrentImage( final Imageimage){ getDisplay().asyncExec( new Runnable(){ public void run(){ flashLabel.setImage(image); } }); } } } 给出一个客户端使用的示例:
Image[]flashs
=
new
Image[]{
ImagesContext.getImage(ImagesContext.MAIL),//ImagesContext是我自己写的一个管理Image的类 ImagesContext.getImage(ImagesContext.MAIL_GRAY)}; ImagestopImage = ImagesContext.getImage(ImagesContext.MAIL); flashImage = new FlashImage(c,SWT.NONE,flashs,stopImage); flashImage.setFlashSpaceTime( 800 ); flashImage.addMouseListener( new MouseAdapter(){ public void mouseDown(MouseEvente){ //do something...... } }); 四、TimeLabel类 时间显示Label则和FlashImage类似,可以说是它的一个简化版
package
cn.com.chengang.myswt;
import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import cn.com.chengang.common.util.CommonUtil; /** * @author chengang2006-4-19 */ public class TimeLabel{ private LabeltimeLabel; private long flashSpaceTime = 480 ; // 闪动间隔时间 private boolean progressStop = false ; // 处理是否停止的标志 private DateFormatdateFormat = new SimpleDateFormat( " HH:mm:ss " ); public TimeLabel(Compositecomp, int style){ timeLabel = new Label(comp,style); timeLabel.setText( " sssssssssssssssssss " ); // 监听Dispose事件,在其销毁时停掉线程 timeLabel.addDisposeListener( new DisposeListener(){ public void widgetDisposed(DisposeEvente){ progressStop = true ; } }); new ShowTimeThread().start(); } public void dispose(){ progressStop = true ; } private DisplaygetDisplay(){ return Display.getDefault(); } private class ShowTimeThread extends Thread{ public void run(){ while ( ! progressStop){ Stringstr = dateFormat.format( new 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(); // } // } } } private void setText( final Stringtime){ getDisplay().asyncExec( new Runnable(){ public void run(){ timeLabel.setText(time); } }); } } public void setLayoutData(GridDatadata){ timeLabel.setLayoutData(data); } public void setDateFormate(DateFormatformat){ dateFormat = format; } } 客户端的使用代码示例:
TimeLabeltimeLabel
=
new
TimeLabel(topComp,SWT.NONE);
// timeLabel.setDateFormate(newSimpleDateFormat("HH:mm:ss")); timeLabel.setLayoutData( new GridData(GridData.END,GridData.CENTER, true , true )); 作者简介陈刚,广西桂林人,著作有《Eclipse从入门到精通》 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |