11.4 进行影响推测的工具
我们最重要的筹码便是对编程语言的认识。每门语言当中都存在所谓的“防火墙”,即能够阻止影响继续传播的语言结构。若能知道这些“防火墙”分别是什么,我们便清楚什么时候不必穿越它们去追溯影响的足迹了。
假设我们想要修改下面这个Coordinate类的表现形式,想要将它泛化成一个能够表示三维和四维坐标的坐标类,为此我们打算改用向量来存储各坐标分量。然而,如果这个类是像下面这样来实现的话,在追踪修改所带来的影响时就无需考虑这个类的代码之外了。
public class Coordinate {
private double x = 0;
private double y = 0;
public Coordinate() {}
public Coordinate(double x, double y) {
this.x = x; this.y = x;
}
public double distance(Coordinate other) {
return Math.sqrt(
Math.pow(other.x - x, 2.0) + Math.pow(other.y - y, 2.0));
}
}
而对于下面这个实现来说就不是这样了:
public class Coordinate {
double x = 0;
double y = 0;
public Coordinate() {}
public Coordinate(double x, double y) {
this.x = x; this.y = x;
}
public double distance(Coordinate other) {
return Math.sqrt(
Math.pow(other.x - x, 2.0) + Math.pow(other.y - y, 2.0));
}
}
上面这两个实现的区别很细微。在第一个实现当中,x和y变量是私有的。而在第二个实现里则是包作用域的。所以,对于第一个实现来说,我们对于x和y变量的任何改动只能通过distance()成员方法来对类的客户代码造成影响,不管其客户代码使用的是Coordinate还是它的某个子类。而在第二个实现中就不同了,与Coordinate类位于同一个包内的代码可以直接访问它的x和y成员变量。于是我们就得关心一下这些代码了,可以将这两个变量也设为私有,从而确保没有客户代码能够直接访问到它们。此外,Coordinate的子类也可能会使用到这两个成员变量,因此我们同样不得不照顾到它的所有子类,看看其中有没有使用了x或y的。
对所用语言的了解和把握是十分关键的,一些微妙的语言规则经常会把我们弄得晕头转向。比如下面这个C++类:
class PolarCoordinate : public Coordinate {
public:
PolarCoordinate();
double getRho() const;
double getTheta() const;
};
在C++中,当const修饰符跟在方法声明的后面时,被声明的方法便不能修改其所属对象的实例变量。但真的是这样吗?假设PolarCoordinate的父类看起来如下:
class Coordinate {
protected:
mutable double first, second;
};
当C++中的mutable关键字用于修饰一个(变量)声明时,便意味着该变量可以被const方法所修改。不可否认,mutable的这种用法非常古怪,然而当我们面对的是一个不甚了解的程序,需要弄清哪些会改变而哪些不会改变的时候,不管用法多古怪,也得硬起头皮进行影响分析。在C++中不作认真检查而简单地把const当作真正的常性是危险的。像这类能够被“绕过去”的语言特性都要加以注意。
了解你所用的语言。







