11.3 影响的传播
代码修改所产生的影响的传播方式有的难以察觉,有的则明显一些。在上一节中的InMemoryDirectory例子中,我们最后的任务是寻找返回值给调用方的方法。尽管一开始是从修改点开始跟踪修改所产生的影响的,我通常还是会先注意到那些带有返回值的方法。除非它们的返回值没有被使用,否则便会将影响传播到它们的调用者那儿。
影响也可能会悄无声息地以不易觉察的方式传播。如果我们有一个对象,而该对象又以另一个对象为参数的话,前者便可能修改后者的状态,而这一修改则会在应用当中的其他地方体现出来。
关于方法的参数是如何被对待(传递)的,每门语言都有不同的规则。许多时候默认的做法便是按值传递对象的引用。这也正是Java和C#的默认做法。这种做法的关键在于我们并不是将对象本身传递给一个方法,而是传递它的一个“句柄(Handle)”。这一事实带来的结果便是,任何方法都可以通过它们接受到的句柄来修改相应对象的状态。此外,有些语言也会提供一些关键字来指出一个句柄只能用于读取而不能用于修改它所指向的对象的状态。例如C++中的const关键字,当将它用于方法形参声明中时,就能够起到上述作用。
代码影响代码的最难以觉察的方式便是通过全局或静态数据了,如下所示:
public class Element {
private String name;
private String text = "";
public Element(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void addText(String newText) {
text += newText;
View.getCurrentDisplay().addText(newText);
}
public String getText() {
return text;
}
}
上面这个类跟我们在InMemoryDirectory中用到的那个Element类几乎一模一样,只不过有一行代码不同:addText中的第二行代码。光看Element的成员方法的签名根本无助于我们发现元素的改变对视图产生的影响。信息隐藏是件好事,只不过,若是被隐藏的是我们需要知道的信息就不妙了。
影响在代码中的传递有三种基本途径:
(1) 调用方使用被调用函数的返回值。
(2) 修改传参传进来的对象,且后者接下来会被使用到。
(3) 修改后面会被用到的静态或全局数据。
不过,有些语言中也有其他途径。例如,在面向方面(aspect-oriented)的语言中,程序员可以编写所谓的“方面”代码,后者能够影响系统中其他地方的代码行为。
我在寻找修改造成的影响时会使用如下的启发式方法:
(1) 确定一个将要修改的方法。
(2) 如果该方法有返回值,查看它的调用方。
(3) 看看该方法是否修改了什么值。是则查看其他使用了这些值的方法,以及使用了这些方法的方法。
(4) 别忘了查看父类和子类,它们也可能使用了这些实例变量和方法。
(5) 查看这些方法的参数,看看你要修改的代码是否使用了某参数对象或它的方法所返回的对象。
(6) 找出到目前为止被你所找出的任何方法修改的全局变量和静态数据。







