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

本节书摘来自华章出版社《驯服烂代码:在编程操练中悟道》一书中的第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所示。

image

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

image

现在从上往下看看这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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值