在第 3 章我们描述了数据抽象的多个阶段,重点是 scope 机制下控制名称的可见性。我们从全局变量开始,它的生命周期贯穿程序执行期。然后讨论局部变量,它的生命周期限制在单个 subroutine 执行期间;嵌套 scope,允许 subroutine 自己为局部的;静态变量,它的生命周期也贯穿程序执行期,但是名称仅在单个 subroutine 可见。随后是模块,允许一组 subroutine 共享一组静态变量;模块类型,允许开发者实例化多个给定抽象;类,允许开发者定义一族相关抽象。
原始的模块鼓励“管理者”风格的编程,模块到处为抽象类型。模块类型和类允许模块自己就是抽象类型。这种区别在两个方面。首先,显示 create
destroy
过程需要从管理者模块导出,可以被模块类型实例的创建和销毁代替。其次,模块实例的方法调用代替了将导出类型作为参数传递的过程调用。类就是建立在模块作为类型机制上,然后增加了继承机制,继承允许通过扩展或者修改现有的抽象实现新的抽象,以及动态方法绑定,这可以使得新版本的抽象执行新的行为,或者在上下文中使用早版本的内容。一个类的实例被称为对象,基于类的语言或者编程称为面向对象。
在第 3 章中对于数据抽象的逐步演化是组织想法的很有用的方法,但是并不能完整反映语言特性的发展。尤其是,对于面向对象编程的发展是基于模块发展的描述是不准确的。相反,面向对象编程的三个基本概念(封装,继承和动态方法绑定)都起源于18世纪60年代中期的 Simula 语言。对于现代的面向对象语言,Simula 在数据封装上显得比较弱,在1970年代 Clu, Modula, Euclid 在这个方面做出了重要贡献。同时,继承和动态方法绑定结合的思想也发生在 1970 年代。
Smalltalk 采用了独特的“基于消息”的编程模型,具有动态类型和不常见的语法。动态类型使得实现相对较慢,而且延迟了错误报告。该语言还紧密整合到了图形编程环境中,因此很难跨系统移植。由于这些原因,并没有被广泛使用,并对后面的语言产生了影响。比如 Eiffle, C++,Ada 95, Fortran 2003, Java,C# 在很大程度上是 Smalltalk 中继承和动态方法绑定和“主流”命令式语言词法和语法的重新融合。另一条线是,Object-C 结合了 Smalltalk 风格的消息和动态类型,并以相对纯粹的方式结合了 C 的语法进行对象操作。对象的方向在函数式语言中也变得重要起来,比如 Lisp 对象系统,和 OCaml 的对象。
最近,动态类型对象重新在 Python 和 Ruby 中获得了知名度,同时静态类型对象作为 Object-C 的继任者(跟随 OCmal)也出现在了 Scala ,Go 和 Swift 中,将动态类型对象建立在静态类型语言中。
在 10.1 中我们概述面向对象编程以及三个主要概念。在 10.2 中讨论数据隐藏和封装的细节。然后在 10.3 中讨论对象初始化和 finalization,以及 10.4 中的动态方法绑定。在 10.6 中(大多数内容在网站上这里也有)考虑多重继承。正如我们看到的多重继承引入了一些特别棘手的语法和实现挑战。最后,在10.7中,我们重新审视面向对象的定义,考虑语言应该或者在多大程度上讲所有事物作为对象进行建模。我们大多数讨论集中在 Smalltalk,Eiffel,C++,Java,只会少量提及其他语言。我们在 14.4.4 中会重新回到动态类型对象的讨论。