版权归原作者所有,如有侵权请与我联系
第1章 对象导论
“我们之所以将自然办分解,组织成各种概念,并按其含义分类,主要是因为我们是整个口语交流社会共同遵守的协定的参与者,这个协定以语言的形式固定下来......除非赞成这个协定中规定的有关语言信息的组织和分类,否则我们根本无法交谈。”
——Benjamin Lee Whorf(1897~1941)
计算机革命起源于机器,因此,编程语言的产生也始于对机器的模仿。
但是,计算机并非只是机器那么简单。计算机是头脑的延伸的工具(就像Steve Jobs常喜欢说的“头脑的自行车”一样),同时还是一种不同类型的表达媒体。因此,这种工具看起来已经越来越不像机器,而更像我们头脑的一部分,以及一种如写作、绘画、雕刻、动画、电影第一样的表达形式。面向对象程序设计(Object-oriented Programming,OOP)便是这种以计算机作为表达媒体的大趋势跌组成部分。
本章将向读者介绍包括开发方法概述在内的OOP的基本概念。本章,乃至本书中,都假设读者已经具备了某些编程经验(当然不一定是C的)。如果读者认为在阅读本书之前还需要在程序设计方面多做些准备,那么就应该去研读可以从www.MindView.net网站上下载的《C编程思想》(Thinking in c)的多媒体资料。
本章介绍的是背景性的和补充性的材料。许多人在没有了解面向对象程序设计的全貌之前,感觉无法轻松自在地做事此类编程。因此,此处将引入许多概念,以期帮助读者扎实地了解OOP。然而,还有些人可能在看到上体结构之前,无法了解面向对象程序设计的全貌,这些人如果设有代码在手,就会陷于困境并最终迷失方向。如果你属于后面的这个群体,并且渴望尽快获取Java语言的细节,那么可以先越过本章——在此处越过本章并不会妨碍你编写程序和学习语言。但是,你最终还是要回到本章来补充所学知识,这样才能够了解到对象的重要性,以及怎样使用对象进行设计。
1.1 抽象过程
所有编程语言都是提供抽象机制。可以认为,人们所能够解决的问题的复杂性直接取决于抽象的类型和质量。所谓的”类型"是指“所抽象的是什么?”汇编语言是对底层机器的轻微抽象。接着出现的许多所谓“命令式”语言(如FORTRAN、BASIC、C等)都是对汇编语言的抽象。这些语言在汇编语言甚而上有了大幅的改进,但是它们所作的主要抽象仍要求在解决问题时要基于计算机的结构,而不是基于所要解决的问题的结构来考虑。程序员必须建立起在机器模型(位于“解空间”内,这是你对问题建模的地方,例如计算机)和实际待解决的模型(位于“问题空间”内,这是问题存在的地方,例如一项任务)之间的关联。建立这种映射是费力的,而且这不是属于编程语言所固有的功能,这使得程序难以编写,并且维护代价高昂,同时也产生了作为副产物的整个“编程方法”行业。
另一种对机器建模的方式就是只针对待解问题建模。早期的编程语言,如LISP和APL,都选择考虑世界的某些特定视图(分别对应于“所有问题最终都是列表”或者“所有问题都是算法形式的”)。PROLOG则将所有问题都转换成决策链。此外还产生了基于约束条件编程的语言和专门通过对图形符号操作来实现编程的语言(后者被证明限制性过强)。这些方式对于它们所在解决的特定类型的问题都是不错的解决方案,但是一旦超出其特定领域,它们就力不从心了。
面向对象方式通过向程序员提供表示问题空间中的元素的工具而更进了一起。这种表示方式非常通用,使得程序员不会受限于任何特定类型的问题。我们将问题空间中的元素及其在解空间中的表示称为“对象”。(你还需要一些无法类比为问题空间元素的对象。)这种思想的实质是:程序可以通过添加新类型的对象使自身适用于某个特定问题。因此,当你在阅读描述解决方案的代码的同时,也是在阅读问题的表达。相比以前我们所使用的语言,这是一种更灵活和更强有力的语言抽象。所以,OOP允许根据问题来描述问题,而不是根据运行解决方案的计算机来描述问题。但是它仍然与计算机在联系:每个对象看起来都有点像一台微型计算机——它具有状态,还具有操作,用户可以要求对象执行这些操作。如果要对现实世界中的对象作类比,那么说它们都具有特性和行为似乎不错。
Alan Kay 曾经总结了第一个成功的面向对象语言、同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了一种纯粹的面向对象程序设计方式:
1)万物皆为对象。将对象视为奇特的变量,它可以存储数据,除此之外,你还可以要求它在自身上执行操作。理论上讲,你可以抽取待求解问题的任何概念化构件(狗、建筑物、服务等),将其表示为程序中的对象。
2)程序是对象的集合,它们通过发送消息来行程彼此所要做的。要想请求一个对象,就必须对该对象发送一条消息。更具体地说,可以把消息想你为对某个特定对象的方法的调用请求。
3)每个对象都有自己的由其他对象所构成的存储。换包话说,可以通过创建包含现有对象的包的方式来创建新类型的对象。因此,可以在程序中构建复杂的体系,同时将其复杂性隐藏在对象的简单性背后。
4)每个对象都拥有其类型。按照通用的说法,“每个对象都是某个类(class)的一个实例(instance)”,这个“类”就是“类型”的同义词。每个类最重要的区别于其他类的特性就是“可以发送什么样的消息给它”。
5)某一特定类型的所有对象都可以接收同样的消息。这是一句意味深长的表述,你在稍后便会看到。因为“圆形”类型的对象同时也是“几何形”类型的对象,所以一个“圆形”对象必定能够接受发送给“几何形”对象的消息。这意味着可以编写与“几何形”交互并自动处理所有与几何形性质相关的事物的代码。这种可替代性(substitutability)是OOP中最强有力的概念之一。
Boochc对对象提出了一个更加简洁的描述:对象具有状态、行为和标识。这意味着每一个对象都可以拥有内部数据(它们给出了该对象的状态)和方法(它们产生行为),并且每一个对象都可以唯一地与其他对象区分开来,具体说来,就是每一个对象在内存中都有一个唯一的地址。
1.2 每个对象都有一个接口
亚里力多德大概是第一个深入研究类型(type)的哲学家,他曾提出过鱼类和鸟类这样的概念。所有的对象都是唯一的,但同时也是具有相同的特性和行为的对象所归属的类的一部分。这种思想被直接应用于第一个面向对象语言Simula-67,它在程序中使用基本关键字class来引入新的类型。Simula,就像其名字一样,是为了开发诸如经典的“银行出纳员问题"(bank teller problem)这样的仿真程序而创建的。在银行出纳员问题中,有出纳、客户、账户、交易和货币单位等许多”对象“。在程序执行期间具有不同的状态而其他方面都相似的对象会被分组到对象的类中,这就是关键字class的由来。创建抽象数据类型(类)是面向对象程序设计的基本概念之一。抽象数据类型的运行方式与内置(built-in)类型几乎完全一致:你可以创建某一类型的变量(按照面向对象的说法,称其为对象或实例),然后操作这些变量(称其为发送消息或请求;发送消息,对象就知道要做什么)。每个类的成员或元素都具有某种共性:每个账户都有不同的结余金额,每个出纳都有自己的姓名。因此,出纳、客户、账户、交易等都可以在计算机程序中被表示成唯一的实体,这些实体就是对象,每一个对象都属于定义了特性和行为的某个特定的类。
所以,尽管我们在面向对象程序设计中实际上进行的是创建新的数据类型,但事实上所有的面向对象程序设计语言都使用了class这个关键词来表示数据类型。当看到类型一词时,可将其作为类来考虑,反之亦然。
因为类描述了具有相同特性(数据元素)和行为(功能)的对象集合,所以一个类实际上就是一个数据类型,例如所有浮点型数字具有相同的特性和行为集合。二者的差异在于,程序员通过定义类来适应问题,而不再被迫使用现有的用来表示机器中的存储单元的数据类型。可以根据需要,通过添加新数据类型来扩展编程语言。编程系统接受新类,并且像对待内置类型一样地暖巢管家它们和进行类型检查。面向对象方法并不是仅局限于构建仿真程序。无论你是否赞成以下观点,即任何程序都是你所设计的系统的一种仿真,面向对象技术的应用确实可以将大量的问题很容易地降解为一个简单的解决方案。
——Benjamin Lee Whorf(1897~1941)
计算机革命起源于机器,因此,编程语言的产生也始于对机器的模仿。
但是,计算机并非只是机器那么简单。计算机是头脑的延伸的工具(就像Steve Jobs常喜欢说的“头脑的自行车”一样),同时还是一种不同类型的表达媒体。因此,这种工具看起来已经越来越不像机器,而更像我们头脑的一部分,以及一种如写作、绘画、雕刻、动画、电影第一样的表达形式。面向对象程序设计(Object-oriented Programming,OOP)便是这种以计算机作为表达媒体的大趋势跌组成部分。
本章将向读者介绍包括开发方法概述在内的OOP的基本概念。本章,乃至本书中,都假设读者已经具备了某些编程经验(当然不一定是C的)。如果读者认为在阅读本书之前还需要在程序设计方面多做些准备,那么就应该去研读可以从www.MindView.net网站上下载的《C编程思想》(Thinking in c)的多媒体资料。
本章介绍的是背景性的和补充性的材料。许多人在没有了解面向对象程序设计的全貌之前,感觉无法轻松自在地做事此类编程。因此,此处将引入许多概念,以期帮助读者扎实地了解OOP。然而,还有些人可能在看到上体结构之前,无法了解面向对象程序设计的全貌,这些人如果设有代码在手,就会陷于困境并最终迷失方向。如果你属于后面的这个群体,并且渴望尽快获取Java语言的细节,那么可以先越过本章——在此处越过本章并不会妨碍你编写程序和学习语言。但是,你最终还是要回到本章来补充所学知识,这样才能够了解到对象的重要性,以及怎样使用对象进行设计。
1.1 抽象过程
所有编程语言都是提供抽象机制。可以认为,人们所能够解决的问题的复杂性直接取决于抽象的类型和质量。所谓的”类型"是指“所抽象的是什么?”汇编语言是对底层机器的轻微抽象。接着出现的许多所谓“命令式”语言(如FORTRAN、BASIC、C等)都是对汇编语言的抽象。这些语言在汇编语言甚而上有了大幅的改进,但是它们所作的主要抽象仍要求在解决问题时要基于计算机的结构,而不是基于所要解决的问题的结构来考虑。程序员必须建立起在机器模型(位于“解空间”内,这是你对问题建模的地方,例如计算机)和实际待解决的模型(位于“问题空间”内,这是问题存在的地方,例如一项任务)之间的关联。建立这种映射是费力的,而且这不是属于编程语言所固有的功能,这使得程序难以编写,并且维护代价高昂,同时也产生了作为副产物的整个“编程方法”行业。
另一种对机器建模的方式就是只针对待解问题建模。早期的编程语言,如LISP和APL,都选择考虑世界的某些特定视图(分别对应于“所有问题最终都是列表”或者“所有问题都是算法形式的”)。PROLOG则将所有问题都转换成决策链。此外还产生了基于约束条件编程的语言和专门通过对图形符号操作来实现编程的语言(后者被证明限制性过强)。这些方式对于它们所在解决的特定类型的问题都是不错的解决方案,但是一旦超出其特定领域,它们就力不从心了。
面向对象方式通过向程序员提供表示问题空间中的元素的工具而更进了一起。这种表示方式非常通用,使得程序员不会受限于任何特定类型的问题。我们将问题空间中的元素及其在解空间中的表示称为“对象”。(你还需要一些无法类比为问题空间元素的对象。)这种思想的实质是:程序可以通过添加新类型的对象使自身适用于某个特定问题。因此,当你在阅读描述解决方案的代码的同时,也是在阅读问题的表达。相比以前我们所使用的语言,这是一种更灵活和更强有力的语言抽象。所以,OOP允许根据问题来描述问题,而不是根据运行解决方案的计算机来描述问题。但是它仍然与计算机在联系:每个对象看起来都有点像一台微型计算机——它具有状态,还具有操作,用户可以要求对象执行这些操作。如果要对现实世界中的对象作类比,那么说它们都具有特性和行为似乎不错。
Alan Kay 曾经总结了第一个成功的面向对象语言、同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了一种纯粹的面向对象程序设计方式:
1)万物皆为对象。将对象视为奇特的变量,它可以存储数据,除此之外,你还可以要求它在自身上执行操作。理论上讲,你可以抽取待求解问题的任何概念化构件(狗、建筑物、服务等),将其表示为程序中的对象。
2)程序是对象的集合,它们通过发送消息来行程彼此所要做的。要想请求一个对象,就必须对该对象发送一条消息。更具体地说,可以把消息想你为对某个特定对象的方法的调用请求。
3)每个对象都有自己的由其他对象所构成的存储。换包话说,可以通过创建包含现有对象的包的方式来创建新类型的对象。因此,可以在程序中构建复杂的体系,同时将其复杂性隐藏在对象的简单性背后。
4)每个对象都拥有其类型。按照通用的说法,“每个对象都是某个类(class)的一个实例(instance)”,这个“类”就是“类型”的同义词。每个类最重要的区别于其他类的特性就是“可以发送什么样的消息给它”。
5)某一特定类型的所有对象都可以接收同样的消息。这是一句意味深长的表述,你在稍后便会看到。因为“圆形”类型的对象同时也是“几何形”类型的对象,所以一个“圆形”对象必定能够接受发送给“几何形”对象的消息。这意味着可以编写与“几何形”交互并自动处理所有与几何形性质相关的事物的代码。这种可替代性(substitutability)是OOP中最强有力的概念之一。
Boochc对对象提出了一个更加简洁的描述:对象具有状态、行为和标识。这意味着每一个对象都可以拥有内部数据(它们给出了该对象的状态)和方法(它们产生行为),并且每一个对象都可以唯一地与其他对象区分开来,具体说来,就是每一个对象在内存中都有一个唯一的地址。
1.2 每个对象都有一个接口
亚里力多德大概是第一个深入研究类型(type)的哲学家,他曾提出过鱼类和鸟类这样的概念。所有的对象都是唯一的,但同时也是具有相同的特性和行为的对象所归属的类的一部分。这种思想被直接应用于第一个面向对象语言Simula-67,它在程序中使用基本关键字class来引入新的类型。Simula,就像其名字一样,是为了开发诸如经典的“银行出纳员问题"(bank teller problem)这样的仿真程序而创建的。在银行出纳员问题中,有出纳、客户、账户、交易和货币单位等许多”对象“。在程序执行期间具有不同的状态而其他方面都相似的对象会被分组到对象的类中,这就是关键字class的由来。创建抽象数据类型(类)是面向对象程序设计的基本概念之一。抽象数据类型的运行方式与内置(built-in)类型几乎完全一致:你可以创建某一类型的变量(按照面向对象的说法,称其为对象或实例),然后操作这些变量(称其为发送消息或请求;发送消息,对象就知道要做什么)。每个类的成员或元素都具有某种共性:每个账户都有不同的结余金额,每个出纳都有自己的姓名。因此,出纳、客户、账户、交易等都可以在计算机程序中被表示成唯一的实体,这些实体就是对象,每一个对象都属于定义了特性和行为的某个特定的类。
所以,尽管我们在面向对象程序设计中实际上进行的是创建新的数据类型,但事实上所有的面向对象程序设计语言都使用了class这个关键词来表示数据类型。当看到类型一词时,可将其作为类来考虑,反之亦然。
因为类描述了具有相同特性(数据元素)和行为(功能)的对象集合,所以一个类实际上就是一个数据类型,例如所有浮点型数字具有相同的特性和行为集合。二者的差异在于,程序员通过定义类来适应问题,而不再被迫使用现有的用来表示机器中的存储单元的数据类型。可以根据需要,通过添加新数据类型来扩展编程语言。编程系统接受新类,并且像对待内置类型一样地暖巢管家它们和进行类型检查。面向对象方法并不是仅局限于构建仿真程序。无论你是否赞成以下观点,即任何程序都是你所设计的系统的一种仿真,面向对象技术的应用确实可以将大量的问题很容易地降解为一个简单的解决方案。