[TDD开发的全过程] 三、抽取公共类

来源:互联网 发布:python 异常重试 编辑:程序博客网 时间:2024/06/13 20:24

                    文/陈刚  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,所以只能用多线程轮换图片的方式来实现图标闪烁,我想这样的类提取出来,用作还是挺大的。

3-1.jpg



三、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 Label flashLabel;
    
private Image stopImage;
    
private Image[] flashImages;
    
private long flashSpaceTime = 100//闪动间隔时间

    
private boolean progressStop = true//处理是否停止的标志

    
/**
     * 
@param comp
     * 
@param style 与Label的style相同
     * 
@param flashImages 闪动的图像
     * 
@param stopImage 停止时的图像
     
*/
    
public FlashImage(Composite comp, int style, Image[] flashImages, Image stopImage) {
        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(DisposeEvent e) {
                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 Display getDisplay() {
        
return Display.getDefault();
    }

    
public void addMouseListener(MouseListener listener) {
        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 Image image) {
            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) };
                Image stopImage 
= ImagesContext.getImage(ImagesContext.MAIL);

                flashImage 
= new FlashImage(c, SWT.NONE, flashs, stopImage);
                flashImage.setFlashSpaceTime(
800);
                flashImage.addMouseListener(
new MouseAdapter() {
                    
public void mouseDown(MouseEvent e) {
                            //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 chengang 2006-4-19
 
*/
public class TimeLabel {
    
private Label timeLabel;
    
private long flashSpaceTime = 480//闪动间隔时间
    private boolean progressStop = false//处理是否停止的标志
    private DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    
public TimeLabel(Composite comp, int style) {
        timeLabel 
= new Label(comp, style);
        timeLabel.setText(
"sssssssssssssssssss");
        
//监听Dispose事件,在其销毁时停掉线程
        timeLabel.addDisposeListener(new DisposeListener() {
            
public void widgetDisposed(DisposeEvent e) {
                progressStop
=true;
            }
        });
        
new ShowTimeThread().start();
    }

    
public void dispose() {
        progressStop 
= true;
    }

    
private Display getDisplay() {
        
return Display.getDefault();
    }

    
private class ShowTimeThread extends Thread {
        
public void run() {
            
while (!progressStop) {
                String str 
= 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 String time) {
            getDisplay().asyncExec(
new Runnable() {
                
public void run() {
                    timeLabel.setText(time);
                }
            });
        }
    }

    
public void setLayoutData(GridData data) {
        timeLabel.setLayoutData(data);
    }

    
public void setDateFormate(DateFormat format) {
        dateFormat 
= format;
    }

}


客户端的使用代码示例:
                TimeLabel timeLabel = new TimeLabel(topComp, SWT.NONE);
//                timeLabel.setDateFormate(new SimpleDateFormat("HH:mm:ss"));
                timeLabel.setLayoutData(new GridData(GridData.END, GridData.CENTER, truetrue));


作者简介

陈刚,广西桂林人,著作有《Eclipse从入门到精通》
您可以通过其博客了解更多信息和文章:http://www.ChenGang.com.cn
版权声明:本博客所有文章仅适用于非商业性转载,并请在转载时注明出处及作者的署名