面向对象的概念
面向对象(Object Oriented,OO)是当前计算机界关心的重点,它是90年代软件开发方法的主流。面
向对象的概念和应用已超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界
面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
谈到面向对象,这方面的文章非常多。但是,明确地给出对象的定义或说明对象的定义的非常少——
至少我现在还没有发现。其初,“面向对象”是专指在程序设计中采用封装、继承、抽象等设计方法。
可是,这个定义显然不能再适合现在情况。面向对象的思想已经涉及到软件开发的各个方面。如,
面向对象的分析(OOA,Object Oriented Analysis),面向对象的设计(OOD,Object Oriented Design)、
以及我们经常说的面向对象的编程实现(OOP,Object Oriented Programming)。许多有关面向对
象的文章都只是讲述在面向对象的开发中所需要注意的问题或所采用的比较好的设计方法。看这些
文章只有真正懂得什么是对象,什么是面向对象,才能最大程度地对自己有所裨益。这一点,恐怕
对初学者甚至是从事相关工作多年的人员也会对它们的概念模糊不清。
面向对象是当前计算机界关心的重点,它是90年代软件开发方法的主流。面向对象的概念和应用已
超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平
台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
一、传统开发方法存在问题
1.软件重用性差
重用性是指同一事物不经修改或稍加修改就可多次重复使用的性质。软件重用性是软件工程追求的
目标之一。
2.软件可维护性差
软件工程强调软件的可维护性,强调文档资料的重要性,规定最终的软件产品应该由完整、一致的
配置成分组成。在软件开发过程中,始终强调软件的可读性、可修改性和可测试性是软件的重要的
质量指标。实践证明,用传统方法开发出来的软件,维护时其费用和成本仍然很高,其原因是可修
改性差,维护困难,导致可维护性差。
3.开发出的软件不能满足用户需要
用传统的结构化方法开发大型软件系统涉及各种不同领域的知识,在开发需求模糊或需求动态变化
的系统时,所开发出的软件系统往往不能真正满足用户的需要。
用结构化方法开发的软件,其稳定性、可修改性和可重用性都比较差,这是因为结构化方法的本质
是功能分解,从代表目标系统整体功能的单个处理着手,自顶向下不断把复杂的处理分解为子处理,
这样一层一层的分解下去,直到仅剩下若干个容易实现的子处理功能为止,然后用相应的工具来描
述各个最低层的处理。因此,结构化方法是围绕实现处理功能的“过程”来构造系统的。然而,用户
需求的变化大部分是针对功能的,因此,这种变化对于基于过程的设计来说是灾难性的。用这种方
法设计出来的系统结构常常是不稳定的,用户需求的变化往往造成系统结构的较大变化,从而需要
花费很大代价才能实现这种变化。
二、面向对象的基本概念
(1)对象。
对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示
具体的事物,还能表示抽象的规则、计划或事件。
(2)对象的状态和行为。
对象具有状态,一个对象用数据值来描述它的状态。
对象还有操作,用于改变对象的状态,对象及其操作就是对象的行为。
对象实现了数据和操作的结合,使数据和操作封装于对象的统一体中
(3)类。
具有相同或相似性质的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以
说类的实例是对象。
类具有属性,它是对象的状态的抽象,用数据结构来描述类的属性。
类具有操作,它是对象的行为的抽象,用操作名和实现该操作的方法来描述。
(4)类的结构。
在客观世界中有若干类,这些类之间有一定的结构关系。通常有两种主要的结构关系,即一般--具
体结构关系,整体--部分结构关系。
①一般——具体结构称为分类结构,也可以说是“或”关系,或者是“is a”关系。
②整体——部分结构称为组装结构,它们之间的关系是一种“与”关系,或者是“has a”关系。
(5)消息和方法。
对象之间进行通信的结构叫做消息。在对象的操作中,当一个消息发送给某个对象时,消息包含接
收对象去执行某种操作的信息。发送一条消息至少要包括说明接受消息的对象名、发送给该对象的
消息名(即对象名、方法名)。一般还要对参数加以说明,参数可以是认识该消息的对象所知道的
变量名,或者是所有对象都知道的全局变量名。
类中操作的实现过程叫做方法,一个方法有方法名、参数、方法体。消息传递如图10-1所示。
二、面向对象的特征
(1)对象唯一性。
每个对象都有自身唯一的标识,通过这种标识,可找到相应的对象。在对象的整个生命期中,它的
标识都不改变,不同的对象不能有相同的标识。
(2)分类性。
分类性是指将具有一致的数据结构(属性)和行为(操作)的对象抽象成类。一个类就是这样一种抽象,
它反映了与应用有关的重要性质,而忽略其他一些无关内容。任何类的划分都是主观的,但必须与
具体的应用有关。
(3)继承性。
继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类
的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己
的内容,并加入若干新的内容。
继承性是面向对象程序设计语言不同于其它语言的最重要的特点,是其他语言所没有的。
在类层次中,子类只继承一个父类的数据结构和方法,则称为单重继承。
在类层次中,子类继承了多个父类的数据结构和方法,则称为多重继承。
在软件开发中,类的继承性使所建立的软件具有开放性、可扩充性,这是信息组织与分类的行之有
效的方法,它简化了对象、类的创建工作量,增加了代码的可重性。
采用继承性,提供了类的规范的等级结构。通过类的继承关系,使公共的特性能够共享,提高了软
件的重用性。
(4)多态性(多形性)
多态性使指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,
收到同一消息可以产生不同的结果,这种现象称为多态性。
多态性允许每个对象以适合自身的方式去响应共同的消息。
多态性增强了软件的灵活性和重用性。
三、面向对象的要素
(1)抽象。
抽象是指强调实体的本质、内在的属性。在系统开发中,抽象指的是在决定如何实现对象之前的对
象的意义和行为。使用抽象可以尽可能避免过早考虑一些细节。
类实现了对象的数据(即状态)和行为的抽象。
(2)封装性(信息隐藏)。
封装性是保证软件部件具有优良的模块性的基础。
面向对象的类是封装良好的模块,类定义将其说明(用户可见的外部接口)与实现(用户不可见的
内部实现)显式地分开,其内部实现按其具体定义的作用域提供保护。
对象是封装的最基本单位。封装防止了程序相互依赖性而带来的变动影响。面向对象的封装比传统
语言的封装更为清晰、更为有力。
(3)共享性
面向对象技术在不同级别上促进了共享
同一类中的共享。同一类中的对象有着相同数据结构。这些对象之间是结构、行为特征的共享关系。
在同一应用中共享。在同一应用的类层次结构中,存在继承关系的各相似子类中,存在数据结构和
行为的继承,使各相似子类共享共同的结构和行为。使用继承来实现代码的共享,这也是面向对象
的主要优点之一。
在不同应用中共享。面向对象不仅允许在同一应用中共享信息,而且为未来目标的可重用设计准备
了条件。通过类库这种机制和结构来实现不同应用中的信息共享。
4.强调对象结构而不是程序结构
四、面向对象的开发方法
目前,面向对象开发方法的研究已日趋成熟,国际上已有不少面向对象产品出现。面向对象开发方
法有Coad方法、Booch方法和OMT方法等。
1.Booch方法
Booch最先描述了面向对象的软件开发方法的基础问题,指出面向对象开发是一种根本不同于传统的
功能分解的设计方法。面向对象的软件分解更接近人对客观事务的理解,而功能分解只通过问题空
间的转换来获得。
2.Coad方法
Coad方法是1989年Coad和Yourdon提出的面向对象开发方法。该方法的主要优点是通过多年来大系统
开发的经验与面向对象概念的有机结合,在对象、结构、属性和操作的认定方面,提出了一套系统
的原则。该方法完成了从需求角度进一步进行类和类层次结构的认定。尽管Coad方法没有引入类和
类层次结构的术语,但事实上已经在分类结构、属性、操作、消息关联等概念中体现了类和类层次
结构的特征。
3.OMT方法
OMT方法是1991年由James Rumbaugh等5人提出来的,其经典著作为“面向对象的建模与设计”。
该方法是一种新兴的面向对象的开发方法,开发工作的基础是对真实世界的对象建模,然后围绕这
些对象使用分析模型来进行独立于语言的设计,面向对象的建模和设计促进了对需求的理解,有利
于开发得更清晰、更容易维护的软件系统。该方法为大多数应用领域的软件开发提供了一种实际的、
高效的保证,努力寻求一种问题求解的实际方法。
4.UML(Unified Modeling Language)语言
软件工程领域在1995年~1997年取得了前所未有的进展,其成果超过软件工程领域过去15年的成就
总和,其中最重要的成果之一就是统一建模语言(UML)的出现。UML将是面向对象技术领域内占主导
地位的标准建模语言。
UML不仅统一了Booch方法、OMT方法、OOSE方法的表示方法,而且对其作了进一步的发展,最终统一
为大众接受的标准建模语言。UML是一种定义良好、易于表达、功能强大且普遍适用的建模语言。它
融入了软件工程领域的新思想、新方法和新技术。它的作用域不限于支持面向对象的分析与设计,
还支持从需求分析开始的软件开发全过程。
五、面向对象的模型
·对象模型
对象模型表示了静态的、结构化的系统数据性质,描述了系统的静态结构,它是从客观世界实体的
对象关系角度来描述,表现了对象的相互关系。该模型主要关心系统中对象的结构、属性和操作,
它是分析阶段三个模型的核心,是其他两个模型的框架。
1.对象和类
(1) 对象。
对象建模的目的就是描述对象。
(2) 类。
通过将对象抽象成类,我们可以使问题抽象化,抽象增强了模型的归纳能力。
(3) 属性。
属性指的是类中对象所具有的性质(数据值)。
(4) 操作和方法。
操作是类中对象所使用的一种功能或变换。类中的各对象可以共享操作,每个操作都有一个目标对
象作为其隐含参数。
方法是类的操作的实现步骤。
2.关联和链
关联是建立类之间关系的一种手段,而链则是建立对象之间关系的一种手段。
(1) 关联和链的含义。
链表示对象间的物理与概念联结,关联表示类之间的一种关系,链是关联的实例,关联是链的抽象。
(2) 角色。
角色说明类在关联中的作用,它位于关联的端点。
(3) 受限关联。
受限关联由两个类及一个限定词组成,限定词是一种特定的属性,用来有效的减少关联的重数,限
定词在关联的终端对象集中说明。
限定提高了语义的精确性,增强了查询能力,在现实世界中,常常出现限定词。
(4) 关联的多重性。
关联的多重性是指类中有多少个对象与关联的类的一个对象相关。重数常描述为“一”或“多”。
图10-8表示了各种关联的重数。小实心圆表示“多个”,从零到多。小空心圆表示零或一。没有符号
表示的是一对一关联。
3.类的层次结构
(1) 聚集关系。
聚集是一种“整体-部分”关系。在这种关系中,有整体类和部分类之分。聚集最重要的性质是传递
性,也具有逆对称性。
聚集可以有不同层次,可以把不同分类聚集起来得到一颗简单的聚集树,聚集树是一种简单表示,
比画很多线来将部分类联系起来简单得多,对象模型应该容易地反映各级层次,图10-10表示一个关
于微机的多极聚集。
(2)一般化关系。
一般化关系是在保留对象差异的同时共享对象相似性的一种高度抽象方式。它是“一般---具体”的关
系。一般化类称为你类,具体类又能称为子类,各子类继承了交类的性质,而各子类的一些共同性
质和操作又归纳到你类中。因此,一般化关系和继承是同时存在的。一般化关系的符号表示是在类
关联的连线上加一个小三角形,如图10-11
4.对象模型
(1)模板。模板是类、关联、一般化结构的逻辑组成。
(2)对象模型。
对象模型是由一个或若干个模板组成。模板将模型分为若干个便于管理的子块,在整个对象模型和
类及关联的构造块之间,模板提供了一种集成的中间单元,模板中的类名及关联名是唯一的。
·动态模型
动态模型是与时间和变化有关的系统性质。该模型描述了系统的控制结构,它表示了瞬间的、行为
化的系统控制
性质,它关心的是系统的控制,操作的执行顺序,它表示从对象的事件和状态的角度出发,表现了
对象的相互行为。
该模型描述的系统属性是触发事件、事件序列、状态、事件与状态的组织。使用状态图作为描述工
具。它涉及到事件、状态、操作等重要概念。
1.事件
事件是指定时刻发生的某件事。
2.状态
状态是对象属性值的抽象。对象的属性值按照影响对象显著行为的性质将其归并到一个状态中去。
状态指明了对象
对输入事件的响应。
3.状态图
状态图是一个标准的计算机概念,他是有限自动机的图形表示,这里把状态图作为建立动态模型的
图形工具。
状态图反映了状态与事件的关系。当接收一事件时,下一状态就取决于当前状态和所接收的该事件,
由该事件引起的状态变化称为转换。
状态图是一种图,用结点表示状态,结点用圆圈表示;圆圈内有状态名,用箭头连线表示状态的转
换,上面标记事件名,箭头方向表示转换的方向。
·功能模型
功能模型描述了系统的所有计算。功能模型指出发生了什么,动态模型确定什么时候发生,而对象
模型确定发生的客体。功能模型表明一个计算如何从输入值得到输出值,它不考虑计算的次序。功
能模型由多张数据流图组成。数据流图用来表示从源对象到目标对象的数据值的流向,它不包含控
制信息,控制信息在动态模型中表示,同时数据流图也不表示对象中值的组织,值的组织在对象模
型中表示。图10-15给出了一个窗口系统的图标显示的数据流图。
数据流图中包含有处理、数据流、动作对象和数据存储对象。
1.处理
数据流图中的处理用来改变数据值。最低层处理是纯粹的函数,一张完整的数据流图是一个高层处
理。
2.数据流
数据流图中的数据流将对象的输出与处理、处理与对象的输入、处理与处理联系起来。在一个计算
机中,用数据流来表示一中间数据值,数据流不能改变数据值。
3.动作对象
动作对象是一种主动对象,它通过生成或者使用数据值来驱动数据流图。
4.数据存储对象
数据流图中的数据存储是被动对象,它用来存储数据。它与动作对象不一样,数据存储本身不产生
任何操作,它只响应存储和访问的要求。
六、面向对象的分析
面向对象分析的目的是对客观世界的系统进行建模。本节以上面介绍的模型概念为基础,结合“银行
网络系统”的具体实例来构造客观世界问题的准确、严密的分析模型。
分析模型有三种用途:用来明确问题需求;为用户和开发人员提供明确需求;为用户和开发人员提
供一个协商的基础,作为后继的设计和实现的框架。
(一)面向对象的分析
系统分析的第一步是:陈述需求。分析者必须同用户一块工作来提炼需求,因为这样才表示了用户
的真实意图,其中涉及对需求的分析及查找丢失的信息。下面以“银行网络系统”为例,用面向对象
方法进行开发。
银行网络系统问题陈述:设计支持银行网络的软件,银行网络包括人工出纳站和分行共享的自动出
纳机。每个分理处用分理处计算机来保存各自的帐户,处理各自的事务;各自分理处的出纳站与分
理处计算机通信,出纳站录入帐户和事务数据;自动出纳机与分行计算机通信,分行计算机与拨款
分理处结帐,自动出纳机与用户接口接受现金卡,与分行计算机通信完成事务,发放现金,打印收
据;系统需要记录保管和安全措施;系统必须正确处理同一帐户的并发访问;每个分处理为自己的
计算机准备软件,银行网络费用根据顾客和现金卡的数目分摊给各分理处。
图10-18给出银行网络系统的示意图。
(二)建立对象模型
首先标识和关联,因为它们影响了整体结构和解决问题的方法,其次是增加属性,进一步描述类和
关联的基本网络,使用继承合并和组织类,最后操作增加到类中去作为构造动态模型和功能模型的
副产品。
1.确定类
构造对象模型的第一步是标出来自问题域的相关的对象类,对象包括物理实体和概念。所有类在应
用中都必须有意义,在问题陈述中,并非所有类都是明显给出的。有些是隐含在问题域或一般知识
中的。
按图10-19所示的过程确定类
查找问题陈述中的所有名词,产生如下的暂定类。
软件银行网络出纳员自动出纳机分行
分处理分处理计算机帐户事务出纳站
事务数据分行计算机现金卡用户现金
收据系统顾客费用帐户数据
访问安全措施记录保管
根据下列标准,去掉不必要的类和不正确的类。
(1)冗余类:若两个类表述了同一个信息,保留最富有描述能力的类。如"用户"和"顾客"就是重
复的描述,因为"顾客"最富有描述性,因此保留它。
(2)不相干的类:除掉与问题没有关系或根本无关的类。例如,摊派费用超出了银行网络的范围。
(3)模糊类:类必须是确定的,有些暂定类边界定义模糊或范围太广,如"记录保管"就模糊类,
它是"事务"中的一部分。
(4)属性:某些名词描述的是其他对象的属性,则从暂定类中删除。如果某一性质的独立性很重
要,就应该把他归属到类,而不把它作为属性。
(5)操作:如果问题陈述中的名词有动作含义,则描述的操作就不是类。但是具有自身性质而且
需要独立存在的操作应该描述成类。如我们只构造电话模型,"拨号"就是动态模型的一部分而不是
类,但在电话拨号系统中,"拨号"是一个重要的类,它日期、时间、受话地点等属性。
在银行网络系统中,模糊类是"系统"、"安全措施"、"记录保管"、"银行网络"等。属于属性的有:"
帐户数据"、"收据"、"现金"、"事务数据"。属于实现的如:"访问"、"软件"等。这些均应除去。
2.准备数据字典
为所有建模实体准备一个数据字典。准确描述各个类的精确含义,描述当前问题中的类的范围,包
括对类的成员、用法方面的假设或限制。
3.确定关联
两个或多个类之间的相互依赖就是关联。一种依赖表示一种关联,可用各种方式来实现关联,但在
分析模型中应删除实现的考虑,以便设计时更为灵活。关联常用描述性动词或动词词组来表示,其
中有物理位置的表示、传导的动作、通信、所有者关系、条件的满足等。从问题陈述中抽取所有可
能的关联表述,把它们记下来,但不要过早去细化这些表述。
下面是银行网络系统中所有可能的关联,大多数是直接抽取问题中的动词词组而得到的。在陈述中,
有些动词词组表述的关联是不明显的。最后,还有一些关联与客观世界或人的假设有关,必须同用
户一起核实这种关联,因为这种关联在问题陈述中找不到。
银行网络问题陈述中的关联:
·银行网络包括出纳站和自动出纳机;
·分行共享自动出纳机;
·分理处提供分理处计算机;
·分理处计算机保存帐户;
·分理处计算机处理帐户支付事务;
·分理处拥有出纳站;
·出纳站与分理处计算机通信;
·出纳员为帐户录入事务;
·自动出纳机接受现金卡;
·自动出纳机与用户接口;
·自动出纳机发放现金;
·自动出纳机打印收据;
·系统处理并发访问;
·分理处提供软件;
·费用分摊给分理处。
隐含的动词词组:
·分行由分理处组成;
·分理处拥有帐户;
·分行拥有分行计算机;
·系统提供记录保管;
·系统提供安全;
·顾客有现金卡。
基于问题域知识的关联:
·分理处雇佣出纳员;
·现金卡访问帐户。
使用下列标准去掉不必要和不正确的关联:
(1)若某个类已被删除,那么与它有关的关联也必须删除或者用其它类来重新表述。在例中,我
们删除了"银行网络",相关的关联也要删除。
(2)不相干的关联或实现阶段的关联:删除所有问题域之外的关联或涉及实现结构中的关联。如"
系统处理并发访问"就是一种实现的概念。
(3)动作:关联应该描述应用域的结构性质而不是瞬时事件,因此应删除"自动出纳机接受现金卡
","自动出纳机与用户接口"等。
(4)派生关联:省略那些可以用其他关联来定义的关联。因为这种关联是冗余的。银行网络系统
的初步对象图如图10-20所示。其中含有关联。
4.确定属性
属性是个体对象的性质,属性通常用修饰性的名词词组来表示.形容词常常表示具体的可枚举的属性
值,属性不可能在问题陈述中完全表述出来,必须借助于应用域的知识及对客观世界的知识才可以找
到它们。只考虑与具体应用直接相关的属性,不要考虑那些超出问题范围的属性。首先找出重要属
性,避免那些只用于实现的属性,要为各个属性取有意义的名字。按下列标准删除不必要的和不正
确的属性:
(1)对象:若实体的独立存在比它的值重要,那么这个实体不是属性而是对象。如在邮政目录中,
"城市"是一个属性,然而在人口普查中,"城市"则被看作是对象。在具体应用中,具有自身性质的
实体一定是对象。
(2)定词:若属性值取决于某种具体上下文,则可考虑把该属性重新表述为一个限定词。
(3)名称:名称常常作为限定词而不是对象的属性,当名称不依赖于上下文关系时,名称即为一
个对象属性,尤其是它不惟一时。
(4)标识符:在考虑对象模糊性时,引入对象标识符表示,在对象模型中不列出这些对象标识符,
它是隐含在对象模型中,只列出存在于应用域的属性。
(5)内部值:若属性描述了对外不透明的对象的内部状态,则应从对象模型中删除该属性。
(6)细化:忽略那些不可能对大多数操作有影响的属性。
5.使用继承来细化类
使用继承来共享公共机构,以次来组织类,可以用两种方式来进行。
(1)自底向上通过把现有类的共同性质一般化为父类,寻找具有相似的属性,关系或操作的类来
发现继承。例如"远程事务"和"出纳事务"是类似的,可以一般化为"事务"。有些一般化结构常常是
基于客观世界边界的现有分类,只要可能,尽量使用现有概念。对称性常有助于发现某些丢失的类。
(2)自顶向下将现有的类细化为更具体的子类。具体化常常可以从应用域中明显看出来。应用域
中各枚举字情况是最常见的具体化的来源。例如:菜单,可以有固定菜单,顶部菜单,弹出菜单,
下拉菜单等,这就可以把菜单类具体细化为各种具体菜单的子类。当同一关联名出现多次且意义也
相同时,应尽量具体化为相关联的类,例如"事务"从"出纳站"和"自动出纳机"进入,则"录入站"就
是"出纳站"和"自动出纳站"的一般化。在类层次中,可以为具体的类分配属性和关联。各属性和都
应分配给最一般的适合的类,有时也加上一些修正。
应用域中各枚举情况是最常见的具体化的来源。
6.完善对象模型
对象建模不可能一次就能保证模型是完全正确的,软件开发的整个过程就是一个不断完善的过程。
模型的不同组成部分多半是在不同的阶段完成的,如果发现模型的缺陷,就必须返回到前期阶段去
修改,有些细化工作是在动态模型和功能模型完成之后才开始进行的。
(1)几种可能丢失对象的情况及解决办法:
·同一类中存在毫无关系的属性和操作,则分解这个类,使各部分相互关联;
·一般化体系不清楚,则可能分离扮演两种角色的类
·存在无目标类的操作,则找出并加上失去目标的类;
·存在名称及目的相同的冗余关联,则通过一般化创建丢失的父类,把关联组织在一起。
(2)查找多余的类。
类中缺少属性,操作和关联,则可删