第1、2、3、4章传统的测试后行开发过程

本文通过一个酒店时钟同步的案例,介绍了传统测试后行方法的实现过程,包括需求分析、设计、编码及测试阶段,并对该方法进行了反思,指出了其在实际应用中的局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

  • 上大学时,老师说软件就是程序加文档,文档很重要

  • 老师还说,为了客观性,软件开发完成后,应该由不同的人员对其进行测试。——传统的测试后行的方法(test last)

案例(酒店时钟同步调整)

以一个案例说明传统的测试后行的开发过程,也正如前四章题目所述:刻舟求剑的文档——>按图索骥地编写代码——>写main()方法测试一下——>调试一下。

1.需求描述

例如在北京一家酒店的大堂里有5个时钟,分别显示北京、伦敦、莫斯科、悉尼和纽约的时间。其中,伦敦与UTC(Coordinated Universal Time,协调世界时间)时间一致,北京比UTC时间早8个小时,莫斯科比UTC时间早4个小时,悉尼比UTC时间早10个小时,纽约比UTC时间晚5个小时。若所有这些城市的时钟都多少有些走时不准,需要调整时间时,大堂服务员只需调准自己手机上的北京时间,那么墙壁上那5个城市的时间就能相应地自动调整准确。

2.整理需求列表

需求编号内容
REQ01一家北京的酒店里有5个时钟,分别显示北京、伦敦、莫斯科、悉尼和纽约的时间
REQ02伦敦时间与UTC时间一致,北京比UTC时间早8个小时,莫斯科比UTC时间早4个小时,悉尼比UTC时间早10个小时,纽约比UTC时间晚5个小时
REQ03将酒店服务员的手机时间设置为北京时间
REQ04若大堂时钟走时不准,只需调整服务员的手机时间,那么墙壁上相应的5个时钟的时间都能自动调整准确

3.领域词汇表定义

中文词条英文词条含义
城市时钟CityClock酒店大堂墙壁上显示各个城市时间的时钟
手机时钟PhoneClock负责调整大堂时钟的酒店服务员的手机里的时钟
UTC时间UtcTimeCoordinated Universal Time,协调世界时间,是全世界用于调整时间和时钟的主要标准
酒店服务员HotelEmployee酒店里负责调整大堂时钟的服务员
时钟Clock城市时钟和手机时钟抽象出来的时钟类

4.领域模型类图

描述

5.代码块

TimeSubject.java:

import java.util.HashMap;
import java.util.Map;

public abstract class TimeSubject {

    protected Map<String, Clock> clocksMap = new HashMap<>();

    /**
     * 加入一个时钟
     * 
     * @param name
     *            时钟名
     * @param clock
     *            时钟对象
     */
    public void attach(String name, Clock clock) {
        clocksMap.put(name, clock);
    }

    /**
     * 删除一个时钟
     * 
     * @param name
     *            时钟名
     */
    public void detach(String name) {
        clocksMap.remove(name);
    }

    /**
     * 更新所有时钟的时间
     */
    public abstract void notifyAllClocks();
}

UtcTime.java

public class UtcTime extends TimeSubject {

    private int utcZeroTime = 0;

    public int getUtcZeroTime() {
        return utcZeroTime;
    }

    public void setUtcZeroTime(int utcZeroTime) {
        this.utcZeroTime = utcZeroTime;
        notifyAllClocks();
    }

    /**
     * 更新所有时钟的时间
     */
    public void notifyAllClocks() {
        for (Clock clock : super.clocksMap.values()) {
            clock.setLocalTime(clock.toLocalTime(utcZeroTime));
        }
    }

    public void printTimeOfAllClocks() {
        for (String name : super.clocksMap.keySet()) {
            Clock clock = super.clocksMap.get(name);
            System.out.println(name + "的当前时间为" + clock.getLocalTime());
        }
    }
}

Clock.java

public class Clock {

    private int localTime = 0;// 时钟的本地时间
    protected int UTC_OFFSET = 0;// 时钟相对于UTC时间的延迟

    /**
     * @param UTC_OFFSET
     *            时钟所在地区相对于UTC时间的延迟,默认为0
     */
    public Clock(int UTC_OFFSET) {
        this.UTC_OFFSET = UTC_OFFSET;
    }

    public int getLocalTime() {
        return localTime;
    }

    public void setLocalTime(int localTime) {
        this.localTime = localTime;
    }

    /**
     * 由UTC时间得到本时钟的本地时间
     * 
     * @param utcZeroTime
     */
    public int toLocalTime(int utcZeroTime) {
        return (utcZeroTime + UTC_OFFSET + 24) % 24;
    }
}

PhoneClock.java

public class PhoneClock extends Clock {

    private UtcTime utcTime;// UtcTime对象

    /**
     * @param UTC_OFFSET
     *            时钟所在地区相对于UTC时间的延迟,默认为0
     */
    public PhoneClock(int UTC_OFFSET) {
        super(UTC_OFFSET);
    }

    public TimeSubject getUtcTime() {
        return utcTime;
    }

    public void setUtcTime(UtcTime utcTime) {
        this.utcTime = utcTime;
    }

    /**
     * 设置时钟的本地时间
     * 
     * @param localTime
     *            本地时间
     */
    public void setLocalTime(int localTime) {
        super.setLocalTime(localTime);
        utcTime.setUtcZeroTime(localTime - super.UTC_OFFSET);
    }
}

6.写main()方法测试一下

HotelWorldClocksRunner.java

public class HotelWorldClocksRunner {
    public static void main(String[] args) {
        UtcTime utcTime = new UtcTime();
        utcTime.attach("北京", new Clock(8));
        utcTime.attach("伦敦", new Clock(0));
        utcTime.attach("莫斯科", new Clock(4));
        utcTime.attach("悉尼", new Clock(10));
        utcTime.attach("纽约", new Clock(-5));

        PhoneClock phoneClock = new PhoneClock(8);
        phoneClock.setUtcTime(utcTime);

        phoneClock.setLocalTime(9);
        utcTime.printTimeOfAllClocks();
    }
}

输出结果为
这里写图片描述

7.总结与反思(测试后行的弊端)

  • 文档经常与代码缺乏同步造成理解偏差。

    系统开发过程中,由于理解、bug、需求等问题经常对前面设计好的 需求列表、领域词汇表、领域模型类图等文档做修改,这个同步的过程相当费时费力,还不可靠。在项目进度的压力下有几个程序能坚持做这样繁琐的维护工作呢?如果无法坚持同步,那么这就好比“刻舟求剑”,不断变化的代码会让您无法根据刚刚刻下的“记号”找到您需要的“剑”,其效果还不如没有“记号”。

  • 写main方法进行测试无法让计算机自动判断软件行为是否符合预期而导致效率低下

    写main方法进行测试,预期的执行结果是保存在人脑的,每次测试要手工运行main方法,并靠眼镜和大脑去判断main方法的输出结果是否正确,不但劳神费力而且效率低下。

  • 问题产生后没能立即发现,耽误修复时间

    上述问题会等到测试工程师都文档、写测试用例、搭建测试环境、运行测试这乙烯利工作完成后才被测试出来;然后测试工程师兴奋地在bug跟踪系统里填写bug报告,并且贴各种截屏、抓各种log,然后报告给开发经理;开发经理会皱褶眉头地按优先级对这些bug排序,并分配给当初开发这个代码的程序员;于是这个程序员不耐烦地停下手中的工作,打开bug跟踪系统,读冗长的bug报告,看各种截屏和log,然后搭建环境重现问题,并试图回想当初是如何编程的,如果幸运地找到了问题所在,这才能最后修复这个问题。

  • 照搬设计模式导致设计出不必要的抽象,编写出从未被调用的方法造成时间浪费。

    目前父类TimeSubject只有一个子类UtcTime,完全可以把子类UtcTime并入父类TimeSubject中,当将来有TimeSubject新的子类时,比如出现了另一种新的世界计时系统,再提取父类。

  • 程序调试过程无法让计算机替代并自动化的使用,导致代码维护时间剧增

    断点调试虽然能仔细观察变量的取值变化并解决问题,但这个过程很繁琐:首先在IDE中将程序运行起来,然后重现错误,观察错误表现,定位错误,猜测出错点,并设置相应断点,再debug运行程序,观察断点处变量值,分析并查找错误。这个过程很繁琐,每次出现bug都这样繁琐地做一遍debug,那么debug将会像黑洞一样无情地消耗程序员的大量时间和精力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值