UML软件工程组织

OO三步曲之浅析OO的基石(二)
作者:倪 硕  来 源: http://nishuo.35123.net
多态,替换原则,对象切割

多态作为OO中的核心机制之一拥有着丰富的内涵。顾名思义,多态就是一种名称多种形态的意思。其主要有三种形式:函数多态,对象变量多态,泛型多态。函数多态主要包括函数重载(overload)和改写(overriding)。泛型多态(genericity)主要是提供了一种创建通用工具的方法,可以在特定的场合将其特化。在这里,我们重点要考量的是对象变量多态。在理解对象变量多态之前,我们首先了解一下OO核心机制之一的替换原则。静态类型的OOPL的一个特征就是一个变量所包含的值(value)的类型可能并不等于这个变量所声明的类型,在传统的编程语言中并不具备这样的特征,因为我们不可能把声明为整型的变量赋上字符串的变量值。而替换原则发生作用的情况就隐含的描叙了两种不同类型所具有的关联----类型继承。Barbara Liskov曾经这样描叙替换原则以及起作用的类型之间的关联:对于类型为S的每个对象s,存在一个类型为T的对象t,对于根据类型T所定义的所有程序P,如果用对象s替换对象t,程序P的行为保持不变,那么类型S就是类型T的子类型[Liskov 1988]

在理解了多态以及替换原则后,我们可以继续深入理解继承与替换原则相结合所带来的新的观点。可以说继承与替换原则的引入影响了几乎所有的OOPL,包括类型系统,值语义/引用语义,对象内存空间分配等等。下面,我将试图逐步的拨开其中的各种因果。

首先考虑,people a; 这样的代码在编译器中将如何实现?可以肯定是首先将把类型people绑定到对象a上,然后必须为对象a分配空间。同时,我们创建people的子类man,由于man IS A people。根据多态以及替换原则,我们当然可以让对象a保存一个man类型的值(这就是替换原则的表现)。这是一种直观的描叙,但在编程语言的实现过程中就出现一些困难。我们知道继承是一种扩展接口与实现的方式,那么我们就很难保证man类型不对people类型做扩展,而一旦做出扩展,我们如何能用存储people对象的空间去存储man类型的对象值呢?

people a;
man b=new man();
a=b;


这样的代码将首先把b对象进行切割,然后再存储到a对象空间去。然而这并不是我们所期望的。那么,为了支持OOP的继承,多态,替换原则,但却需要避免对象切割的发生,面对对象a我们将采用何种分配空间模型呢?常用的有下面三种方式:

1, 只为a分配基类people的存储空间,不支持对象多态以及替换原则。这样的模型内存分配紧凑,存储效率很高。

2, 分配继承树中的最大类对象所需要空间(在这里是man类的对象值空间),这样的模型简单,同时可以实现多态和替换原则而避免对象切割问题,但是十分浪费内存空间十分明显。

3, 只分配用于保存一个指针所需要的存储空间,在运行时通过堆来分配对象实际类型所需要的空间大小,这样也可以实现多态和替换原则而避免对象切割问题。(也就是说a只是一个对象的引用,而不是真实的对象,真实对象的生成必须靠程序员显式的声明)。

对于上面提到的三种内存模型,1和3都被一些程序设计语言所采用。相信说到这里,大家应该开始慢慢明白了。是的,C++作为C语言的继承者,对于效率的追求迫使它必须采用第一种最小静态空间分配的方式,由于基于栈空间的程序运行效率要比基于堆空间的程序运行效率高出许多,所以C++允许用栈空间保存对象,但同时也允许堆空间保存对象,可以说C++是采用了1和3两种相混合的内存模型,而C++中基于1内存模型的对象,也就是说基于栈内存空间的对象是没有办法体现多态和替换原则的(请思考一下在C++中什么对象是基于栈的),而基于3内存模型的对象将支持多态和替换原则(又想一想在C++中什么对象是基于堆的)。这里,我们终于可以揭开第一层迷雾了,很多人都知道在C++中只有指针和引用才能支持对象的多态行为,但是为什么会如此?上面做出了最好的解释。

Java语言由于设计理念和C++有着很大的区别,它采用的是第3种对象模型,一切对象(除了基本类型对象)都是基于堆分配的。这也是Java语言必须采用虚拟机的原因所在。在C++中很大一部分对象是不需要程序员进行管理的(静态空间对象),而在Java中,如果不采用虚拟机机制,所有的对象都需要程序员管理,而这样的开发代价将是巨大而不现实的。这也就揭开了第二层迷雾,当我们在对比C++和Java语言的时候总是为虚拟机是否有其价值而争论不休,但当你抛开所谓的好与不好的简单讨论,进入到其语言本身的内在对象存储本质的时候,也许对于各种声音才会有一个属于自己的清醒认识。

让我们继续望下走,不同的对象内存分配模型直接影响到其程序设计语言的赋值的含义。在各种编程语言中,赋值可以给出两种不同的语义解释:复制语义和指针语义。很明显,由于C++支持两种相混合的对象存储模型(但是默认的存储方式是栈存储),所以在C++中默认赋值语义采用的是前者,但C++同时提供了指针语义的功能支持(在拷贝构造函数和=运算符重载中用户进行自定义)。而在Java中采用的是后者。这也就是我们揭开的最后一道迷雾,不同的对象存储模型直接导致了不同的赋值语义。

面向对象的计算模型和可计算性

编程就是用计算所需要的指令构成一种运算装置,无论我们的程序设计思想以及程序设计语言如何发展和提高,最终我们所使用的底层计算数学模型并没有改变。但高级程序设计语言给我们带来的变革是在其语言环境中构建起了一个全新的,更抽象的虚拟计算模型。Smalltalk语言引入的对象计算模型从根本上改变了以前的传统计算模型,以前的计算模型突出的是顺序计算过程中的机器状态,而现在的对象计算模型突出的对象之间的协作其计算结果由参加计算的所有的对象的状态总体构成。而由于对象本身具有自身状态,我们也可以把一个对象看成是一个小的计算机器。这样,面向对象的计算模型就演变成了许多小的计算机器的合作计算模型。图灵机作为计算领域内的根本计算模型,精确的抓住了计算的要点:什么是可计算的,计算时间和空间存储大小开销有多大。计算模型清楚的界定了可计算性的范围,也就界定了哪些问题是可求解,哪些问题是不可求解的。OOP为程序员提供了一种更加抽象和易于理解的新的计算模型,但其本身并没有超越冯.诺依曼体系所代表的图灵机数学计算模型。所以我们不能期望OOP能帮助我们解决更多的问题,或者减少运算的复杂度。但OOP却能帮助我们用一种更容易被我们所理解和接受的方式去描叙和解决现实问题。

结 束

这篇文章做为这个系列的第一篇,对于OOP中的许多核心概念和机制进行了有益的讨论,作者衷心的希望通过这篇文章能够让大家对于OOP有更深入的理解,同时明白OOP作为已经发展将近三十年的程序设计思想,其自身丰富的理论内涵不是单单学习几门OOPL就可以领悟。最后期望本文能实现了它的初衷---抛砖引玉。

Reference:

[D&T 1988] : Type Theories and Object-Oriented programming by Scott Danforth and Chris Tomlinson on ACM Computing Surveys Vol.20 No.1 March 1988

[Liskov 1988] :Data Abstraction and Hierarchy by Barbara Liskov on Sigplan Notices,23(5),1988

[C&W 1985] On understanding types,data abstraction,and polymorphism by Cardelli . L and Wegner . P on ACM Computing Surveys Vol.17 No.4 Dec 1985

[B.S 1991] What is “Object-Oriented programming”?(1991 revised version) by Bjarne Stroustrup AT&T Bell Lab Murray Hill ,New Jersey 07974

[B.S 1998] 《The C++ programming language (Special Editon)》 by Bjarne Stroustrup Addison Wesley 1998

[Gamma 1995] 《Design Pattern》by Eric Gamma etc Addison Wesley 1995

[Booch 94] : 《Object-Oriented Analysis and Design with Application (Sec Editon)》 by Grady Booch Addison Wesley 1994

[Jams 2000] : 《The Java programming language (Third Editon)》by Ken Arnold, Jams Gosling, David Holmes Addison Wesley 2000

[Budd 2002]: 《Introduction to Object-Oriented programming (Third Editon)》 by Timothy A.Budd Addison Wesley 2002

参考书籍:

《C++程序设计语言》Bjarne Stroustrup著 裘宗燕译 机械工业出版社 2002年

《设计模式》Erich Gamma等著 李英军等译 机械工业出版社 2000年

《面向对象软件开发原理》Anton著 袁兆山等译 机械工业出版社 2003年

《面向对象编程导论》Timothy A.Budd著 黄明军等译 机械工业出版社 2003年

《面向对象分析与设计》 Grady Booch著 冯博琴等译 机械工业出版社 2003年

《面向对象软件构造》(英文版.第二版) Bertrand Meyer著 机械工业出版社 2003年

版权所有:UML软件工程组织