OO三步曲之浅析OO的基石[原创]
注:文章所有权归作者 倪 硕 所有,欢迎任何形式的转载,不过请注明原始文章作者以及出处地点:http://nishuo.35123.net.并且欢迎访问MatrixCpp的专栏
------------------------------------------
OO三步曲
前言:面向对象程序设计(Object-Oriented Programming,以下简称OOP)是一种起源于六十年代的Simula语言,发展已经将近三十年的程序设计思想。其自身理论已经十分完善,并被多种面向对象程序设计语言(Object-Oriented Programming Language,以下简称OOPL)实现。如果把Unix系统看成是国外在系统软件方面的文化根基,那么Smalltalk语言无疑在OOPL领域和Unix持有相同地位。由于很多原因,国内大部分程序设计人员并没有很深的OOP以及OOPL理论,很多人从一开始学习到工作很多年都只是接触到c/c++,java,vb,delphi等静态类型语言,而对纯粹的OOP思想以及作为OOPL根基的Smalltalk以及动态类型语言知之甚少,不知道其实世界上还有一些可以针对变量不绑定类型的编程语言。而这些对比却是深刻理解OO理论的重要部分,而国内这方面的资料也为数不多。故把自己的一些OO学习心得写下来做为一个系列文章(一共三篇,第一篇描叙OOP的一些基本但容易被误解的理论,第二篇主要说明各种OOPL演化和发展以及对于OOP理论的支持,第三篇主要是说模式和组件在OOP中的地位以及展望OOP的未来),由于文章描叙的只是自己对于OOP/OOPL的理解,错误以及浅薄之处再所难免,只是希望对大家能起到抛砖引玉的作用。
浅析OO的基石
从抽象说起
Booch曾经在他自己的OO领域内的名著[Booch 94]中开篇就论叙到了复杂性是软件开发过程中所故有的特质。而人们处理复杂性的最根本武器就是抽象。广义的抽象代表的是对复杂系统的简化描叙或规格说明,为了突出系统的本质属性而故意忽略其中的非实质性细节。“一个概念只有当能被最终用来实现的机制独立的描叙,理解,分析时,才将这个概念限定为抽象的概念”。而Booch也给出了他心目中关于OO领域内的狭义抽象定义:“抽象表示一个对象与其他所有对象相区别的基本特征,因此提供了同观察者角度有关的清晰定义的概念界限。”因此,根据不同观察角度,我们可以针对OOP给出不同级别的抽象层次。通常,面对一个典型的面向对象程序,[Budd 2002]将其分成五个抽象层,分别覆盖了OOP中的分析,设计与编程的各个阶段:
1, 最高级别的抽象层上,程序被看成是由很多相互作用并且遵守契约的对象所组成的对象集合。对象之间相互合作完成程序的计算任务。这个抽象级别上的典型代表就是设计模式思想(Design Pattern)。
2, 第二个抽象层就是一个对象集单元,也就是一群定义之间有相互联系的对象,在程序设计语言级别来看Java中是packages,C++中是name space。这个抽象级别上的典型代表就是模块化思想(Modularity)。
3, 第三个抽象层所代表的是典型的OOP模式:客户/服务器模型,这主要是用来抽象两个对象之间的互交过程。在这个抽象级别上的典型代表就是对象之间的消息机制(Message Passing)。
4, 第四个抽象层就是针对一组相似对象定义一个类作为生成对象的模板,类定义了对象的对外使用接口以及继承对象所需的内部继承接口,而这个抽象层次的典型代表就是接口编程(Interface Programming)。
5, 第五个抽象层就是实现一个类所需要的方法和成员变量的实现(Implementation)。在这里OOP最终和POP(Procedure-Oriented Programming)相融合。
当然,我们可以根据各自的观察角度划分成更细的抽象层次比如说针对第五层抽象用到的POP理论,我们还可以进一步的划分出控制抽象(三种完全描叙图灵机计算模型所需要的控制结构)以及数据抽象(ADTs)等等,并由此继续下去(如果你的想象力足够丰富的话:)。
什么是OOP?
OOP的许多原始思想都来之于Simula语言,并在Smalltalk语言的完善和标准化过程中得到更多的扩展和对以前的思想的重新注解。可以说OO思想和OOPL几乎是同步发展相互促进的。与函数式程序设计(functional-programming)和逻辑式程序设计(logic-programming)所代表的接近于机器的实际计算模型所不同的是,OOP几乎没有引入精确的数学描叙,而是倾向于建立一个对象模型,它能够近似的反映应用领域内的实体之间的关系,其本质是更接近于一种人类认知事物所采用的哲学观的计算模型。由此,导致了一个自然的话题,那就是OOP到底是什么?[D&T 1988][B.S 1991] .。在OOP中,对象作为计算主体,拥有自己的名称,状态以及接受外界消息的接口。在对象模型中,产生新对象,旧对象销毁,发送消息,响应消息就构成OOP计算模型的根本。
对象的产生有两种基本方式。一种是以原型(prototype)对象为基础产生新的对象。一种是以类(class)为基础产生新对象。原型的概念已经在认知心理学中被用来解释概念学习的递增特性,原型模型本身就是企图通过提供一个有代表性的对象为基础来产生各种新的对象,并由此继续产生更符合实际应用的对象。而原型-委托也是OOP中的对象抽象,代码共享机制中的一种。一个类提供了一个或者多个对象的通用性描叙。从形式化的观点看,类与类型有关,因此一个类相当于是从该类中产生的实例的集合。而这样的观点也会带来一些矛盾,比较典型的就是在继承体系下,子集(子类)对象和父集(父类)对象之间的行为相融性可能很难达到,这也就是OOP中常被引用的---子类型(subtype)不等于子类(subclass)[Budd 2002]。而在一种所有皆对象的世界观背景下,在类模型基础上还诞生出了一种拥有元类(metaclass)的新对象模型。即类本身也是一种其他类的对象。以上三种根本不同的观点各自定义了三种基于类(class-based),基于原型(prototype-based)和基于元类(metaclass-based)的对象模型。而这三种对象模型也就导致了许多不同的程序设计语言(如果我们暂时把静态与动态的差别放在一边)。是的,我们经常接触的C++,Java都是使用基于类的对象模型,但除此之外还有很多我们所没有接触的OOPL采用了完全不一样的对象模型,他们是在用另外一种观点诠释OOP的内涵。
什么是类型(type)?
类型以及类型系统的起源以及研究与发展是独立于OOP的。早在五十年代的FORTRAN语言编译器实现中,就已经采用类型系统作为类型检查的一种手段。广义的类型一般被定义为一种约束,也就是一种逻辑公式。而在对类型的研究过程中产生多种方法,比如[C&W 1985]等。而代数方法(algebraic approach)是一种非常好的建立类型的形式化规范的方法。代数中的一个类型对应于一系列元素,在它们之上定义代数操作。同时在此基础上二阶λ演算已经被用于继承和模板所支持的模型。在上面两种方法中,类型被认为是一系列满足确定约束条件的元素,更抽象的方式可以把一个类型当作规定一个约束条件,如果我们规定的约束条件越好,相对应的被定义元素的集合就越精密,所以逻辑公式(logical formulas)就成为描叙类型特征的最合适工具。在这里,我们不想深入的探究对于类型理论的各种不同的数学模型,我们需要明白的是类型(type)以及类型理论这个在编程语言中经常应用到的概念的内涵是极其丰富的,而其自身理论的发展并非局限于OOP之中,但当两者相结合的时候就对我们的程序观产生了巨大的影响。