background image

 

  清单 1. 

 

典型的 bug 

 —— 

 

模式

使用 mutator 

 

方法配置 factory 方法

1 public class ShoppingCart {
2     private BigDecimal totalCost;
3
4     private boolean qualifiesForFreeShipping() { ... }
5     private BigDecimal getShippingCost() { ... }
6
7     public void checkout() {
8         ...
9         if (!qualifiesForFreeShipping())
10             totalCost.add(getShippingCost()); //WRONG!
11     }
12 }
13

 

  清单 1 

 

中的错误是一种常见的错误,它忘记了对象是不可变的,从而将 factory 

 

方法误认为 mutator 方法。如果在代码中查找此类错误,就会发现存在同一错误多次发生

 

的情况,因为它来源于对特定库类工作方式的误解。对于查找此 bug,负责任的开发人员

 

可能会搜索整个代码基址来查找对 BigDecimal.add()、subtract() 等方法的调用,并寻找忽
略返回值的其他实例。

 

  此策略是一个好的开头,但我们可以做得更好。在这里识别 bug 模式是非常容易

 —— 

忽略不可变对象上的求值方法(value-bearing method)的结果。识别出该模式后,构

建识别此模式的检测器是相对简单的一件事件。(FindBugs 在核心检测器集中有这样一个
检 测 器 。 )  

 

此 技 术 不 仅 可 以 应 用 于 BigDecimal , 还 可 以 应 用 于 其 他 不 可 变 类 ( 如 

BigInteger、String 

 

或 Color)中。

 

  花费一点时间为 bug 

 

模式创建一个 bug 检测器,它会为您带来可观的收益。不仅

 

可以用比手工操作更少的工作和更高的信心来审核整个项目,从中寻找 bug,而且还可
以在现在和将来将同一检测器应用到其他项目中。您已针对不断恢复、随时可能出现的  
bug 

 

类型建立了防御机制,而不是在逐个实例的基础上解决 bug。

 

 

 示例 bug 检测器

 

  为说明编写 FindBugs 检测器的过程,我们编写了一个简单的检测器,它可以查

 

找对 System.gc() 的调用。(

 

下载此示例检测器代码的 源代码。) 

 

虽然调用的 System.gc() 不

 

一定是 bug,但在实践中,它会带来更多的问题(

 

多于它解决的问题 )。尤其是,如果错误

 

地调用了库中隐藏的 System.gc(),则会降低使用该库的应用程序的性能,开发人员可能
会感到很茫然,对性能会如此低下感动很奇怪。

 

  编写 bug 

 

检测器的第一步是识别被检测的 bug 模式。在本例中,该模式非常简单,