《驯服烂代码:在编程操练中悟道》一第3章 写main()方法测试一下

    xiaoxiao2023-06-21  161

    本节书摘来自华章出版社《驯服烂代码:在编程操练中悟道》一书中的第3章,作者 伍斌,更多章节内容可以访问云栖社区“华章计算机”公众号查看

    第3章 写main()方法测试一下

    “类图上的所有类都实现完了。咱们现在可以写个main()方法来测试一下了。”先创建一个包含main()方法的类HotelWorldClocksRunner。HotelWorldClocksRunner类的代码如下所示(CM: Added class HotelWorldClocksRunner with a main() method to have a try.):

    +public class HotelWorldClocksRunner { +}

    然后在这个类里面写main()方法。main()方法的代码如下所示(CM: Added the main() method to class HotelWorldClocksRunner and wrote the expected code there.):

    public class HotelWorldClocksRunner { + public static void main(String[] args) { + TimeSubject utcTime = new UtcTime(); + utcTime.attach("beijing", new CityClock(8)); + utcTime.attach("london", new CityClock(0)); + utcTime.attach("moscow", new CityClock(4)); + utcTime.attach("sydney", new CityClock(10)); + utcTime.attach("newYork", new CityClock(-5)); + Clock phoneClock = new PhoneClock(utcTime); + + phoneClock.setLocalTime(9); + + utcTime.printTimeOfAllClocks(); + } }

    这段代码分3个部分,第1部分是做准备工作;第2部分是调用手机时钟的setLocalTime()方法来设定时间为北京时间上午9点,以触发所有城市时钟的自动调整;第3部分是打印所有时钟的本地时间。在第1部分中,我们先创建一个具有TimeSubject类型的UtcTime实例;再把5个城市时钟的实例都分别attach到这个UtcTime实例上,每创建一个城市时钟实例,都把该城市与UTC时间的时差作为构造器的参数传进这个新创建的实例中。比如北京比UTC时间早8小时,所以在attach北京时钟时,用new CityClock(8)来创建北京时钟实例。最后创建手机时钟实例phoneClock,并把上面准备好的UtcTime实例作为构造器的参数传进去,以便在PhoneClock类的setLocalTime()方法中,调用UtcTime类的setUtcZeroTime()方法,来自动调整所有城市时钟的时间。现在咱们先创建CityClock类的带有时差参数的构造器。“等等!我觉得带有utcTime参数的创建PhoneClock的实例那句话写得有问题。因为PhoneClock和CityClock都继承同一个父类Clock,为何创建CityClock实例时要提供时差参数,而创建PhoneClock实例时却没有提供时差参数?难道PhoneClock的实例都不需要时差参数吗?”问得好!PhoneClock的实例在创建时,确实也和创建CityClock实例时一样,需要传入当地时间与UTC时间之间的时差。需要改一改这个main()方法。修改main()方法中创建PhoneClock实例的代码如下所示(CM: Updated the main() method to make the constructor of PhoneClock is the same with CityClock and add the UtcTime object to the phoneClock using method PhoneClock.setUtcTime().):

    utcTime.attach("moscow", new CityClock(4)); utcTime.attach("sydney", new CityClock(10)); utcTime.attach("newYork", new CityClock(-5)); - Clock phoneClock = new PhoneClock(utcTime); + Clock phoneClock = new PhoneClock(8); + phoneClock.setUtcTime(utcTime); phoneClock.setLocalTime(9);

    在创建PhoneClock实例时,把北京时间距离UTC时间的时差8作为构造器的参数传进去,然后在PhoneClock类上增加一个接口setUtcTime()方法,来把utcTime传给phoneClock实例。“这样应该没问题了。再更改一下类图,在图中把这个更改标记为7号。”在细化后的类图中对7号的更改如图3-1所示。

    这个main()方法有不少编译错误,大概有4处。HotelWorldClocksRunner类的main()方法的4处编译错误如图3-2所示。

    现在从上往下看看这4个编译错误。第1个编译错误是CityClock类还没有一个接受该城市与UTC时间的时差的构造器;第2个编译错误是PhoneClock类也没有接受一个其所在城市与UTC时间的时差的构造器;第3个编译错误是PhoneClock类还没有创建setUtcTime()方法;第4个编译错误是UtcTime类还没有创建printTimeOfAllClocks()方法。接着用Alt+Enter快捷键来让IDEA帮助咱们创建这些默认的构造器和方法。现在先消除第1个编译错误,创建CityClock类的带有时差参数的构造器。创建CityClock类的带有时差参数的构造器的代码如下所示(CM: Added constructor CityClock(int utcOffset).):

    public class CityClock extends Clock { + public CityClock(int utcOffset) { + super(); + } + @Override public void setLocalTime(int localTime) { 在CityClock类的带有时差参数的构造器里,时差参数 u``` tcOffset应该作为参数传进super()方法里,并在父类Clock里也添加一个带时差参数的构造器来接收这个参数。 在CityClock类中将时差参数utcOffset作为参数传进super()方法的代码如下所示(CM: Added constructor Clock(int utcOffset).):

    public class CityClock extends Clock {

    public CityClock(int utcOffset) { super();

    super(utcOffset);

    } 在父类Clock里添加一个带时差参数的构造器的代码如下所示(CM同上):

    public abstract class Clock {

    protected static final int UTC_OFFSET = 0;protected static int UTC_OFFSET; protected int localTime = 0;public Clock(int utcOffset) {UTC_OFFSET = utcOffset;}public abstract void setLocalTime(int localTime); 因为Clock类的成员变量UTC_OFFSET需要在其构造器里赋值,所以就不能是final的了。 再消除第2个编译错误,创建PhoneClock类的带有时差参数的构造器。 创建PhoneClock类的带有时差参数的构造器的代码如下所示(CM: Created constructor PhoneClock(int utcOffset).):

    public class PhoneClock extends Clock {

    private UtcTime utcTime; public PhoneClock(int utcOffset) {super(utcOffset);} + 现在消除第3个编译错误,创建PhoneClock类的setUtcTime()方法。不过在main()方法中,变量phoneClock被声明为Clock类型了。如果是这样的话,setUtcTime()方法应该在父类Clock中创建,而成为一个公共接口,这使得Clock类的另一个子类CityClock也不得不实现这个它并不需要的接口。这就不大合理了。所以可以在main()方法中,把变量phoneClock声明为PhoneClock类型,这样setUtcTime()方法就只在PhoneClock类上创建了。 在main()方法中,把变量phoneClock声明为PhoneClock类型的代码如下所示(CM: Changed type of varialbe phoneClock in main() to be PhoneClock.):

    utcTime.attach("newYork", new CityClock(-5));

    Clock phoneClock = new PhoneClock(8);

    PhoneClock phoneClock = new PhoneClock(8);

    phoneClock.setUtcTime(utcTime); “main()方法里还有一个问题,就是变量utcTime的类型应该是UtcTime,而不应该是其父类TimeSubject。因为根据类图来看,PhoneClock类持有一个UtcTime的实例,以便调用后者的setUtcZeroTime()方法。而这个方法只在UtcTime类中定义了,其父类TimeSubject并没有定义。所以为了调用UtcTime类中的setUtcZeroTime()方法,main()方法里的变量utcTime的类型应该是UtcTime。” 好的,这就改过来。 在main()方法中,把变量utcTime声明为UtcTime类型的代码如下所示(CM: Changed type of varialbe utcTime in main() to be UtcTime.):

    public class HotelWorldClocksRunner {

    public static void main(String[] args) { TimeSubject utcTime = new UtcTime();

    UtcTime utcTime = new UtcTime();

    utcTime.attach("beijing", new CityClock(8)); 现在可以创建PhoneClock类的setUtcTime()方法来消除第3个编译错误了。 在PhoneClock类中创建setUtcTime()方法的代码如下所示(CM: Created method Phone-Clock.setUtcTime(UtcTime).):

    super.localTime = localTime;

    this.utcTime.setUtcZeroTime(localTime - UTC_OFFSET); } public void setUtcTime(UtcTime utcTime) {this.utcTime = utcTime;} } 现在可以创建UtcTime类的printTimeOfAllClocks()方法来消除第4个编译错误了。这个方法专门是为测试用的,所以就不在类图中画出来了。 创建UtcTime类的printTimeOfAllClocks()方法的代码如下所示(CM: Created method UtcTime.printTimeOfAllClocks().):

    clock.setLocalTime(Clock.toLocalTime(this.utcZeroTime));

    } } public void printTimeOfAllClocks() {for (String clockName : super.clocks.keySet()) {System.out.println(clockName + ": " + super.clocks.get(clockName).getTime());}} } 因为utcTime能从其父类TimeSubject里继承Map类型的成员变量clocks,所以就能在printTimeOfAllClocks()方法里写一个循环语句,打印所有的时钟名称和时间。 现在就差创建Clock类的getTime()方法了。 创建Clock类的getTime()方法的代码如下所示(CM: Created method Clock.getTime().):

    public static int toLocalTime(int utcZeroTime) {

    return utcZeroTime + UTC_OFFSET; } public String getTime() {return String.valueOf(this.localTime);} } 看起来现在终于可以运行一下这个main()方法了。在IDEA里打开那个main()方法,把光标定位到类名上,然后按Ctrl+Shift+F10组合键运行一下。结果出来了,奇怪,所有的城市的时间都是9点。 第一次运行main()方法的结果如图3-3所示。 “现在的问题是,除了北京以外,其他所有城市的当地时间都是错的,而且都是9。加个断点调试一下吧。“ 在调试前,咱们先看看这章做了什么工作: 1)开始编写main()方法,并通过打印语句,测试了一下先前按照细化后的类图所编写的代码。 2)在创建PhoneClock的实例时发现并修复了构造器的参数问题。 3)先在main()方法中编写调用了暂时不存在但期望存在的接口的代码(即意图代码),然后循着这些意图代码的红色编译错误,来编写生产代码以消除这些错误。 4)修改细化后的类图以反映设计的修改。 ![image](https://yqfile.alicdn.com/edc16bce0cdc59264450a73d9a69504588e1bb33.png) 相关资源:敏捷开发V1.0.pptx
    最新回复(0)