在我们熟悉的OO语言中,可以通过private、protected、public等访问控制修饰符将数据 和方法分为内部可见、子类可见、外部可见等不同访问级别。本文从一个较为特别的封装相 关例子出发,讨论封装、类型系统、契约式编程相关话题。我们先从例子开始:
public class Person {
private int _money;
public void Change(amount){
this._money += amount;
}
public void Exchange(Person p, int amount) {
p._money -= amount;
this._money += amount;
}
}
基于”没有经过我允许,不许直接动我的钱”的假设,Person类把变量_money 作为private成员,并提供public的Change方法。但有争议的地方在于,在Exchange中我们可 以直接访问并修改p._money。请注意上面的代码在C#编译器下是完全合法的,类似的代码在 C++和Java下也一样。我在第一次接触这个例子的时候,起初有些疑惑编译器是对还是错?后 来想了一个理由“OO的封装是基于类的,而非对象”来说服自己。
现在,对这个问题有了更新的认识,希望和大家讨论。上面的例子中,我们的理想情况是 Person对象本身可以直接访问自己的_money变量,其他Person对象不能直接访问。但OO的封 装实现必须依赖编译器进行静态访问权限检查;既然是静态那就只能应用到类型,无法应用 到对象实例。因此,在上例中C#编译器无法确认p和this是否引用同一对象或不同对象。从这 个意义上讲,OO的封装只到类这一级别与其说是设计考虑不如说是不得以的选择。
从更大的层面上讲,这个例子不仅和OO封装相关,本质上是反映了程序语言类型系统的作 用和局限。程序语言的类型系统是一种静态的跟踪和检查机制,它能保证程序的类型正确性 ,但无法保证语义正确性(理想情况下,上面的例子中,p如果和this是同一对象那么语义正 确;否则,语义错误)。换句话说,类型正确性可以通过类型系统用形式化的方式进行表达 和检查。那么语义正确性应该如何来保证呢?有没有形式化的方式呢?答案也许就像下面这 样:
public class Person {
private int _money;
public void Change(amount){
this._money += amount;
}
public void Exchange(Person p, int amount) {
Assert(this != p);
p.Change(-amount);
this._money += amount;
}
}
增加Assert断言,就是希望从形式上保证语义的正确性。我的理解是:我们需要一种形式 化的方式保证语义的正确性。这是不是就是所谓契约式编程的初衷呢?希望高手指点!