分享
 
 
 

诊断和纠正Java程序中反复出现的错误类型

王朝java/jsp·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

欢迎光临诊断 Java 代码,一个隔周更新的新专栏,着重讨论和您日常编程工作有关的 Java 解决方案。本文为第一篇,介绍了错误模式的概念,一个非常有用的概念,它将提高您检测和修正代码中错误的能力。您会了解到一种最普遍的错误模式,这将为您开始识别和避免更高级的错误模式奠定基础。

错误模式和它们为什么有用

正如好的编程技能涉及很多设计模式(您可以在不同的程序上下文中组合和应用这些模式)的知识一样,好的调试技能也涉及对错误模式的一定了解。错误模式就是已发出的错误和程序中潜在的错误之间的重复出现的相互关系。这种概念对编程来说并不新鲜。医生们在诊断疾病时依靠相似类型的相互关系。他们在实习期间通过和资格较老的医生共同工作来学习这些。他们的教育就是集中在做这种诊断上的。相反,我们软件工程师的教育是集中在过程设计和算法分析上的。这些技能固然重要,但是人们对调试过程的教育却很少关注。相反,我们得自己去“拾起”这种技能。随着极端编程的出现和它对单元测试的注重,这种做法已经开始改变了。但是频繁的单元测试只是解决了问题的一部分。一旦发现错误,就必须诊断和纠正它们。幸运的是,很多错误都遵循我们可以识别的几种错误模式的其中一种。一旦您可以识别出这些错误模式,您就可以诊断出错误的原因并且更快地纠正它了。

错误模式与反模式有关,反模式是一次又一次被证明是失败的公共软件设计的模式。虽然反模式是设计模式,错误模式却是与编程错误相关的错误的程序行为的模式。这与设计根本没有关系,而是与编程和调试过程有关。

通过示例学习

为了说明错误模式后面的思想,让我们来考虑一种基本错误模式,编程新手(经常还有更高级的程序员)常常会遇到这种错误模式。在后面的文章中,我们会谈到更高级的错误模式。对每一种模式,我会讨论将有助于把该模式的错误的发生控制到最少的编程原则(并非暗示所有的错误都是不遵循编程原则的结果;不管我们遵循多少原则,我们都会犯错误)。

为了分类起见,我会使用下面的形式(从医学上借用一些术语)来概括错误模式描述:

模式名称

症状

起因

治疗方法和预防措施

Rogue Tile 模式

也许它是编程新手中最普遍的错误模式,起因是复制和粘贴一段代码到程序的其它部分。有时,复制的一小部分因为功能上需求的略微不同而作了改动。不可避免地,错误在一个副本中被修正了,而在另一个副本中没有被修正,这样在错误症状复发时就会让您很头疼。尽管大多数程序员很快就熟悉了这种错误模式,但他们中很少人采取适当的措施来将这种错误的出现控制到最少。您很容易就会偷懒不去思考而简单地复制您认为已经可以运行的代码。但是工作效率由于修正代码而丧失,这是因为不加选择的复制―粘贴操作很快降低了复制代码带来的任何工作效率。

我称此为 Rogue Tile 模式是因为,一段代码的各个副本可以被看成是分布在程序中的“tile”。由于不同副本中的代码出现了差异,副本就变成了“rogue tile”。

症状

这种错误的模式的最普遍症状是,在您认为已经修正了问题以后,程序还继续表现出错误的行为。

起因

为了理解这种情况发生的原因,我们来看看下面的二元树类层次结构:

public abstract class Tree {

}

public class Leaf extends Tree {

public Object value;

...

}

public class Branch extends Tree {

public Object value;

public Tree left;

public Tree right;

...

}

对于这些类要注意的第一件事就是,两种具体类都包含 Object 类型的 value 字段。如果您决定稍后让树包含,比如说,Interger,您也许会忘记更新其中的一个字段声明。如果程序的其它部分需要这些字段是 Interger 的话,程序就很可能不会编译。您或许记得您改变了其中一个类的 value 字段的类型,却忽略了一个事实,就是您没有在其它类中作相应的改变。

一些预防措施

当然,这个示例所示的错误是编程新手可以很快学会通过分解出公共代码来避免的。在本例中,字段声明应该移到 Tree 类中。它的两个子类就会继承这个字段,而且对字段声明的任何改变都只需要在一个地方出现。

继续看这个示例,我们可能还会编写在一个 Tree 中相加和相乘所有节点的方法。为了简单起见,我将以递归的方式来编写这些方法。

// in class Tree:

public abstract int add();

public abstract int multiply();

// in class Branch:

public int add() {

return this.value.intValue() + left.add() + right.add();

}

public int multiply() {

return this.value.intValue() * left.multiply() + right.multiply();

}

// in class Leaf:

public int add() { return this.value.intValue(); }

public int multiply() { return this.value.intValue(); }

请注意我在 multiply 方法中为 Branch 类引入的错误:我没有用第三项去乘,而是加了它。错误发生了,因为我通过复制 add 方法中的代码并作轻微(但不完全)的改动创建了 multiply 方法。这种错误非常隐蔽,因为调用 multiply 方法永远不会发出错误信号。事实上,在很多情况下,它会返回一个看上去完全合理的结果。

就象以前一样,我们可以通过分解出公共代码来将这种错误控制到最少。在这种情况下,我们可以编写一个单独的方法,它在 Tree 上累计一个运算符(作为一个参数传送)。我们可以使用一种被称为公共模式的设计模式(不是错误模式!)在对象中封装这个运算符。

public abstract class Operator {

public abstract int apply(int l, int r);

}

public class Adder extends Operator {

public int apply(int l, int r) {

return l + r;

}

}

public class Multiplier extends Operator {

public int apply(int l, int r) {

return l * r;

}

}

然后我们就可以如下面的代码所示在我们的 Tree 类层次结构中改变这个方法:

// in class Tree:

public abstract int accumulate(Operator o);

public int add() {

return this.accumulate(new Adder());

}

public int multiply() {

return this.accumulate(new Multiplier());

}

// in class Leaf:

public int accumulate(Operator o) {

return value.intValue();

}

in class Branch:

public int accumulate(Operator o) {

return o.apply(this.value.intValue(),

o.apply(left.accumulate(o),

right.accumulate(o)));

}

通过分解出公共代码,我们消除了在 add 和 multiply 方法正文中出现复制―粘贴错误的可能性。另外,请注意我们不再需要为 Tree 的每一个子类编写单独的 add 和 multiply 方法了。

分解出公共代码是一个很好的习惯,但它并不适用于所有的情况。比如说,Java 类型系统的简单性经常迫使我们在精确类型检验和保持对程序的每个不同的功能性元素的单点控制(请参阅参考资料,阅读我写的关于 NextGen 的文章)之间作出选择。正因为这个,Rogue Tile 模式是所有开发人员必须一直努力以控制到最少的一种错误类型。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有