1.2 面向对象的基本概念本节着重介绍面向对象的基本概念,也对相应的面向对象的技术方法做些说明和解释。
面向对象方法学的基本原则
面向对象方法学认为:客观世界是由各种“对象”所组成的,任何事物都是对象,每一个对象都有自己的运动规律和内部状态,每一个对象都属于某个对象“类”,都是该对象类的一个元素。复杂的对象可以是由相对比较简单的各种对象以某种方式组成的。不同对象的相互作用就构成了我们要研究 分析和构造的客观系统。
面向对象方法学认为:通过类比,发现对象间的相似性,即对象间的共同属性,这就是构成对象类的根据。
面向对象方法学认为:对于已分成类的各个对象,可以通过定义一组“方法”来说明该对象的功能,也即是:允许作用于该对象上的各种操作。对象间的相互联系是通过传递“消息”来完成的。
面向对象方法学比较自然地模拟了人类认识客观世界的方法。即应使描述问题的问题空间和解决问题的方法空间在结构上尽可能地一致。我们分析 设计和实现一个系统的方法尽可能接近我们认识一个系统的方法。
1.2.1 对象、类、消息
面向对象技术是基于对象(object )概念的。下面介绍对象概念。
在现代汉语词典中,对象是行动或思考时作为目标的人或事物。
在思维科学中,对象是客观世界中具有可区分性的、能够唯一标识的逻辑单元。对象所代表的本体可能是一个物理存在,也可能是一个概念存在。
,面向对象”是计算机科学中的一个技术名词,具有其特定的技术含义。从面向对象的观点来看,现实世界是由各式各样独立的、异步的、并发的实体对象组成,每个对象都有各自的内部状态和运动规律,不同对象之间或某类对象之间的相互联系和作用,就构成了各式不同的系统。
面向对象方法是基于客观世界的对象模型化的软件开发方法。在面向对象程序设计中,所谓对象,就是一个属性(数据)集及其操作(行为)的封装体。作为计算机模拟真实世界的抽象,一个对象就是一个实际问题论域,一个物理的实体或逻辑的实体。在计算机程序中,可视为一个“基本程序模块”,因为它包含了数据结构和所提供的相关操作功能。
我们把客观世界的实体称之为问题空间的对象,任何事物都是对象。
我们用计算机解题是借助某种语言规定对计算机实体施加某种动作,以此动作的结果去映射解,我们把计算机实体称之解空间的对象。
在面向对象的程序设计中,“对象”是系统中的基本运行实体。即对象是具有特殊属性(数据)和行为方式(方法)的实体。
从动态的观点看,对象的操作就是对象的行为。问题空间对象的行为是极其丰富的,而解空间对象的行为是极其死板的。因此,只有借助于极其复杂的算法才能操纵解空间对象而得到解。面向对象语言提供了“对象”概念,这样,程序员就可以定义解空间对象。
从存储的角度来看,“对象”是一片私有存储,其中有数据也有方法。其它对象的方法不能直接操纵该对象的私有数据,只有对象私有的方法才可以操纵它。
从对象的实现来看,“对象”是一台自动机,其中私有数据表示了对象的状态,该状态只能由私有的方法改变它。每当需要改变对象的状态时,只能由其它对象向该对象发送消息,对象响应消息后按照消息模式找出匹配的方法,并执行该方法。
对象的属性是指描述对象的数据,可以是系统或用户定义的数据类型,也可以是一个抽象的数据类型,对象属性值的集合称为对象的状态(state)。
对象的行为是定义在对象属性上的一组操作方法(method) 的集合。方法是响应消息而完成的算法,表示对象内部实现的细节,对象的方法集合体现了对象的行为能力。
对象的属性和行为是对象定义的组成要素,有人把它们统称为对象的特性。一般具有以下特征:
(1)具有一个状态,由与相关联的属性值集合所表征。
(2)具有唯一标识名,可以区别于其他对象。
(3)有一组操作方法,每个操作决定对象的一种行为。
(4)对象的状态只能被自身的行为所改变。
(5)对象的操作包括自操作(施于自身)和它操作(施于其他对象)
(6)对象之间以消息传递的方式进行通信。
(7)一个对象的成员仍可以是一个对象。
其中,前3 条是对象的基本特征,后4条是属于特征的进一步定义说明。
有的书上又这样写:
对象的特征
1、模块性:一个对象是一个可以独立存在的实体。从外部看这个模块,只了解这个模块具有哪些功能,模块的内部状态以及如何实现这些功能的细节都是“隐蔽”在模快的内部的。
2、继承性和类比性:人们是通过对客观世界中的各种对象进行分类及合并等方法来认识世界的,每个具体的对象都是在它所属的某一类对象(类)的层次结构中占据一定的位置。下一层次的对象继承了上一层次对象的某些属性。另一方面,不同的对象具有某些相同的属性时,也常常把它们归并成一类。
3、动态连接性:我们把对象和对象间所具有的统一、方便、动态地连接和传递消息的能力与机制称之为动态连接性。
4、易维护性
在面向对象系统中,人们的注意力集中于具有相同特性的一类对象,抽象出这样一类对象共同的结构和行为,进行一般描述,从而避免数据的冗余。“物以类聚”,分类、类比、类型、同类,等等,是人们归纳客观事物的方法。下面介绍类的概念和作用。
类(class)是对象的抽象及描述,是具有共同属性和操作的多个对象的相似特性的统一描述体。类也是对象,是一种集合对象,称之为对象类(object class),简称为类,以有别于基本的实例对象(object instance)。
在类的描述中,每个类要有一个名字,要表示一组对象的共同特征,还必须给出一个生成对象实例的具体方法。类中的每个对象都是该类的对象实例,即系统运行时通过类定义属性初始化可以生成该类的对象实例。实例对象是描述数据结构,每个对象都保存其自己的内部状态,一个类的各个实例对象都能理解该所属类发来的消息。
类提供了完整的解决特定问题的能力,因为类描述了数据结构(对象属性)、算法(方法)和外部接口(消息协议)。
类由方法和数据组成,它是关于对象性质的描述,包括外部特性和内部实现两个方面。类通过描述消息模式及其相应的处理能力来定义对象的外部特性,通过描述内部状态的表现形式及固有处理能力的实现来定义对象的内部实现。一个类实际上定义的是一种对象类型,它描述了属于该类型的所有对象的性质。
对象是在执行过程中由其所属的类动态生成的,一个类可以生成多个不同的对象。同一个类的对象具有相同的性质。一个对象的内部状态只能由其自身来修改。因此,同一个类的对象虽然在内部状态的表现形式上相同,但可有不同的内部状态。从理论上讲,类是一个抽象数据类型的实现。
一个类的上层可以有超类,下层可以有子类,形成一种类层次结构。这种层次结构的一个重要特点是继承性,一个类继承其超类的全部描述。这种继承具有传递性。所以,一个类实际上继承了层次结构中在其上面的所有类的全部描述。因此,属于某个类的对象除具有该类所描述的特性外,还具有层次结构中该类上面所有类描述的全部特性。
抽象类是一种不能建立实例的类。抽象类将有关的类组织在一起,提供一个公共的根,其它的子类从这个根派生出来。抽象类刻画了公共行为的特性并将着些特征传给它的子类。通常一个抽象类只描述与这个类有关的操作接口,或是这些操作的部分实现,完整的实现被留给一个或几个子类。抽象类已为一个特定的选择器集合定义了方法,并且有些方法服从某种语义,所以,抽象类的用途是用来定义一些协议或概念。
综上所述,类是一组对象的抽象,它将该种对象所具有的共同特征集中起来,由该种对象所共享。在系统构成上,则形成了一个具有特定功能的模块和一种代码共享的手段。
在一个有效率的面向对象系统中,是没有完全孤立的对象的,对象的相互作用的模式是采用消息传送来进行的。
消息(message)是面向对象系统中实现对象间的通信和请求任务的操作。消息传递是系统构成的基本元素,是程序运行的基本处理活动。
一个对象所能接受的消息及其所带的参数,构成该对象的外部接口。对象接受它能识别的消息,并按照自己的方式来解释和执行。一个对象可以同时向多个对象发送消息,也可以接受多个对象发来的消息。消息只反映发送者的请求,由于消息的识别、解释取决于接受者,因而同样的消息在不同对象中可解释成不同的行为。
对象间传送的消息一般由三部分组成,即接受对象名、调用操作名和必要的参数。
在C++中,一个对象的可能消息集是在对象的类描述中说明,每个消息在类描述中由一个相应的方法给出,即使用函数定义操作。向对象发送一个消息,就是引用一个方法的过程。实施对象的各种操作,就是访问一个或多个在类对象中定义的方法。
消息协议是一个对象对外提供服务的规定格式说明,外界对象能够并且只能向该对象发送协议中所提供的消息,请求该对象服务。在具体实现上,是将消息分为公有消息和私有消息,而协议则是一个对象所能接受的所有公有消息的集合。
前面讲过,对象间的相互联系是通过传递消息来实现的。消息用来请求对象执行某一处理或回答某些信息的要求,消息统一了数据流和控制流,程序的执行是靠在对象间传递消息来完成的。发送消息的对象称为发送者,接受消息的对象称为接受者。消息中只包含发送者的要求,消息完全由接受者解释,接受者独立决定采用什么方式完成所需的处理。一个对象能接受不同形式不同内容的多个消息,相同形式的消息可以送往不同的对象,不同的对象对于形式相同的消息可以有不同的解释,能够做出不同的反映。对于传来的消息,对象可以返回响应的回答信息,但这种返回并不是必修的。
消息的形式用消息模式刻画,一个消息模式定义了一类消息,它可以对应内容不同的消息。对于同一消息模式的不同消息,同一个对象所做的解释和处理都是相同的,只是处理的结果可能不同。对象固有处理能力按消息分类,一个消息模式定义对象的一种处理能力。所以,只要给出对象的所有消息模式及相应于每一个模式的处理能力,也就定义了一个对象的外部特征。消息模式不仅定义了对象所能受理的消息,而且还定义了对象的固有处理能力。
方法
把所有对象分成各种对象类,每个对象类都定义一组所谓的“方法”,它们实际上可视为允许作用于各对象上的各种操作。
1.2.2 封装性、继承性和多态性在上述面向对象的基本概念的基础上,将就所有面向对象程序设计都具有的3个共同特性进行分析说明,使我们对面向对象的概念和原理能够有进一步的认识和理解。
封装是一种信息隐蔽技术,用户只能见到对象封装界面上的信息,对象内部对用户是隐蔽的。封装的目的在于将对象的使用者和对象的设计者分开,使用者不必知道行为实现的细节,只需用设计者提供的消息来访问该对象。
封装性(encapsulation)是面向对象具有的一个基本特性,其目的是有效地实现信息隐藏原则。这是软件设计模块化、软件复用和软件维护的一个基础。
封装是一种机制,它将某些代码和数据链接起来,形成一个自包含的黑盒子(即产生一个对象)。一般地讲,封装的定义为:
(1)一个清晰的边界,所有的对象的内部软件的范围被限定在这个边界内。封装的基本单位是对象;
(2)一个接口,这个接口描述描述该对象与其他对象之间的相互作用;
(3)受保护的内部实现,提供对象的相应的软件功能细节,且实现细节不能在定义该对象的类之外。
面向对象概念的重要意义在于,它提供了较为令人满意的软件构造的封装和组织方法:以类/对象为中心,既满足了用户要求的模块原则和标准,又满足代码复用要求。客观世界的问题论域及具体成分,在面向对象系统中,最终只表现为一系列的类/对象。
对象的组成成员中含有私有部分、保护部分和公有部分,公有部分为私有部分提供了一个可以控制的接口。也就是说,在强调对象的封装性时,也必须允许对象有不同程序的可见性。可见性是指对象的属性和服务允许对象外部存取和引用的程度。
面向对象程序设计技术鼓励人们把问题论域分解成几个相互关联的子问题,每个子问题(子类)都是一个自包含对象。一个子类(subclass)可以继承父类的属性和方法,还可以拥有自己的属性和方法,子类也能将其特性传递给自己的下一级子类,这种对象的封装、分类层次和继承概念,与人们在对真实世界认识的抽象思维中运用聚合和概括相一致。
面向对象的语言以对象协议或规格说明作为对象的外界面。协议指明该对象所接受的消息,在对象的内部,每个消息响应一个方法,方法实施对数据的运算。对数据方法的描述是协议的实现部分或叫类体。
显式地将对象的定义和对象的实现分开是面向对象系统的一大特色。封装本身即模块性,把定义模块和实现模块分开,就使得用面向对象技术所开发设计的软件的维护性、修改性在为改善。
继承性(inheritance) 是面向对象技术中的另一个重要概念和特性,它体现了现实中对象之间的独特关系。既然类是对具体对象的抽象,那么就可以有不同级别的抽象,就会形成类的层次关系。若用结点表示类对象,用连接两结点的无向边表示其概括关系,就可用树形图表示类对象的层次关系(父类、子类)。继承关系可分为以下几种:一代或多代继承、单继承(single inheritance)和多继承(multiple inheritance)。子类仅对单个直接父类的继承叫做单继承。子类对多于一个的直接父类的继承叫多继承。如图1.1所示。(P7)
多重继承:如果一个类可以直接继承多个类,则这种继承方式称为多重继承。
简单继承:如果限制一个类至多只有一个超类,这种方式称为简单继承。
就继承风格而言,还有全部继承、部分继承,等等。一般的面向对象系统,在不同程度上支持如下四种类型的继承:
(1)替代继承,如果能够对类T的对象比类E的对象实施更多的操作,就说类T继承类E,即在类E的对象处能够用类T的对象来替代。这种继承是基于方法而非值。
(2)包含继承,如果类T的每个对象也是类E的对象,则说类T是类E的子对象。这种继承是基于结构而非操作。
(3)限制继承,如果类E包括满足某种已知限定条件的类T的所有对象,则类T是类E的一个子类。这是包含继承的特殊情形。
(4)特化继承,如果类E的对象是类T的对象,而T带有更多特殊信息,则类T是类E的子类。
继承性允许程序设计人员在设计新类时,只须考虑与父类所不同的特性部分,而继承父类的内容为自己的组成部分。如果父类中某些行为不适用于子类,则程序设计人员可在子类中重写方法的实现。因此继承机制不仅除去基于层次联系的类的共性的重复说明,提高代码复用率,而且能使开发者的大部分精力用于系统中新的或特殊的部分设计,便于软件的演进和增量式扩充。
继承性是自动的共享类、子类和对象中的方法和数据的机制。
每个对象都是某个类的实例,一个系统中类对象是各自封闭的。如果没有继承机制,则类对象中数据和方法就可能出现大量的重复。
继承性是实现从可重用成分构造软件系统的最有效的特征,它不仅支持系统的可重用性,而且还促进系统的可扩充性。
面向对象技术的第3个特性是多态性(polymorphism)。多态性原意是指一种具有多种形态有事物,这里是指同一消息为不同的对象所接受时,可导致不同的行为。多态性支持“同一接口,多种方法”,使高层代码(算法)只写一次而在低层可多次复用。面向对象的多种多态性方法的使用,如动态绑定(dynamic binding)、重载(overload)等,提高了程序设计的灵活性和效率。
所谓多态即一名字可具有多种语义。在面向对象的语言中,多态引用表示可引用多个类的实例。由于多态具有可表示对象的多个类的能力,因而,它既与动态类型有关又与静态类型有关。
在C++中,利用多态性概念,使用函数名和参数类别来实现功能重载,即使用不同参数就可以使之同各个对象相结合,分别实现相应的任务。
1.2.3 常用术语简释和定义从面向对象方法学原理出发,对上述的有关术语可以进一步地做如下的简要释义:
信息(information):是对事物的一种表示和描述。
软件(software):是描述信息处理的信息。
对象(object):是一个由信息及有关对它进行处理的描述所组成的包。
对象可以定义如下:
OBJ::=<ID,MS,DS,MI>
其中ID是对象标识或名称;DS是属性数据;MS是对象受理的操作集合(方法);MI是对象处理的消息集。
消息(message):是对某种对象处理的说明。消息可以定义为
MSG::=<MN,ARG>
其中MN是消息名;ARG是变元组。
类(class):是对一个或几个相似对象的描述;类可以定义为一个四元组:
CLS::=<ID,INH,DD,OP,ITF>
其中,ID是类的标识或称为名字;INH是类的继承性描述;DD是属性数据;OP是操作集合,即方法集;ITF是统一的对外接口或协议。
继承(inherient)是类的特性,即子类可以继承父类(前辈类)的特性。可用一个偏序关系定义继承的特性:
INH=<C,>=)
其中,C为处于继承链上所有的类,>=表示继承关系继承是有传递性的,即
IF(C2,>=C1)&(C3>=C2) THEN C3>=C1
在对继承作了以上定义后,还可以对类作重新的定义
类n::=<类n的ID,∪类i本身的数据结构描述,∪类i本身的操作实现,∪类i本身的对外接口> (i=1,2,…,n)
其中类1到类n是属于同一类链的,且,类i继承类i+1的特性。
元类(metaclass):当某个类的单个实例本身就是一个类时,这个类就称为元类。
子类(subclass):是在共享其他类的描述后,再对这个描述做某些修改而构成的类。
实例(instance):是被某个类所描述的对象中的一个具体对象。
对象实例可以定义为一个三元组:
OB::=<OID,ODY,CID>
其中,OID是对象名或标识;ODY是对象实例的描述;CID是对象所属类的标识。
方法(method):是关于对象在接受到消息后所采取的一系列操作的描述,是对象对消息的响应。
方法字典(method dictionary):是消息选择符和方法之间的一个相联集合。
面向对象方法(object-oriented methods),是一种运用对象、类、消息传递、继承、封装、聚合、多态性等概念来构造软件系统的软件开发方法。
1.2.4 概念内涵的区别在面向对象的基本概念中,有两对表达形式相似而内涵不同的术语,在此也做一些比较和说明。
类型(type):在面向对象系统中,类型概括了具有相同特性的一组对象的特征,是抽象数据类型的概念。它由接口和实现两部分组成,接口对用户是可见的,而实现部分只有设计人员可见。接口部分包括一组操作及用法说明,实现部分数据(是对象内部结构的描述)和操作(实现接口部分的过程)。C++等系统是支持类型概念的。
类(class),类的说明与类型相同,但概念上有所不同,类以方法表现出其动态性,并且包含了“对象生成器”(执行NEW操作产生新对象)和“对象存储器”(或叫对象仓库,表示类的一组实例对象,也称类的外延)的概念。
“类型”在常规程序设计语言中的作用主要体现在数据描述上,作为保证程序正确性检查和提高程序效率的工具。而类的作用在于作为模拟手段,以统一的方式构造现实世界模型,因而提高属于系统的最高层,并且可在运行时刻操作。
关于消息传递和过程调用,形式上相似,但有3点本质不同:
(1)消息传递必须给出关于通道信息,即要显示地指明接受方,而过程调用的信道则是隐含的,其适用范围取决于变元;
(2)消息传递接受方是一实体,具有保持状态的能力,而过程调用则没有此要求;
(3)消息传递可以是异步的,因而是并发的,过程调用的本质是串行的。
下面对面向对象技术的原则,过程等做一个小结。
1、面向对象方法在处理复杂问题时的基本原则
(1)对所研究的系统在不同的层次上抽象为一些对象,及用互通“消息”的方法在对象间进行联系。
(2)对用户提供对象(模块)外特性的描述,而隐蔽基内部的实现细节。
(3)下一层次的对象可自动继承一上层次对象的某些特性。
(4)在处理复杂问题时,应特别注意各个对象间的共性和异性,这是对问题归纳或演绎的依据。
2、面向对象方法提供了统一的表示范式
(1)从系统设计者的角度来看,希望对各种系统的问题域描述,对系统内各组成部分的功能和数据描述,以及对于系统对外接口的描述都有统一的表示范式。
(2)从系统分析者和系统实现者的角度来看,希望用于系统分析的表示范式和用于系统设计的表示范式应尽可能一致。
(3)从用户(对系统提出需求)和设计者(满足用户的需求)来看,希望能相互理解,能逐步地和同步地明确需求和实现系统,尽量少用“瀑布”模型的设计流程,即必须当前一步的要求提得十分明确时,才进行下一步的工作。
(4)从OOA到OOD到OOP,都力求使用统一的表示范式。
3、用面向对象方法分析系统的一般过程
(1)研究的是什么问题,属于哪个问题域。
(2)调查、询问、收集材料、考查以往的结果。
(3)分析该问题在整个系统中的位置,它和上下层次的关系(属性的继承)。
(4)根据所要研究问题的层次、详略,抽象出一些对象及对象间的联系(消息),完成问题(系统)的结构描述。
4、用面向对象的方法设计系统的一般过程
(1)定义对象的属性及描述。
(2)每个对象和其类的关系(哪些是可继承的共性,哪些是本对象的特性)。
(3)定义对象间的通信机制。
(4)确定每个对象的状态。
5、用面向对象的方法进行程序设计
(1)用面向对象的程序设计语言、工具(环境)实现OOA及OOD的结果,实现对象的内部的机理和细节。
(2)用面向对象的快速原型法对系统进行优化。
(3)大量提高软件的可重用性。