2.2 面向对象开发方法
2.2.1 面向对象开发方法简介
面向对象开发方法(Object Oriented Method,OOM)是在各种面向对象程序设计方法的基础上逐步发展起来的一种新的系统开发方法。在面向对象的软件系统开发过程中,应首先进行面向对象的分析(Object Oriented Analyze,OOA),其次进行面向对象的设计(Object Oriented Design,OOD),然后进行面向对象的编程(Object Oriented Program,OOP)和测试(Object Oriented Test,OOT),但是面向对象开发方法的发展历程,却是先有OOP,然后发展到OOD、OOA和OOT。
20世纪80年代初发布的SmallTalk-80是第一个比较完善的面向对象编程语言(Object Oriented Programming Language,OOPL),随后涌现出一大批OOPL,标志着面向对象方法走向了实用。此时人们认识到面向对象的系统开发不仅仅是编程问题,在采用OOPL所提供的类、对象等元素和封装、继承等机制来编写程序之前,必须先用面向对象的观点进行构思和设计,这一认识促使人们将面向对象的思想向前推进了一大步——从单纯地解决编程问题推进到设计领域。G.Booch于1982年首次使用了面向对象的设计,1986年,他又较完整地阐述了面向对象的开发思想,其后相继出现了一批面向对象的开发方法,使得面向对象方法自20世纪90年代初开始得到广泛的应用。
2.2.2 面向对象的基本概念
1.问题领域
系统开发是一个问题求解过程,开发一个软件系统的目的就是为了解决某个(或某一组)特定的问题,而问题领域就是指软件系统所模拟的真实世界中的系统,如银行的自动存取款系统、学校的学籍管理系统、制造企业的生产管理系统等。
2.对象
对象是对问题领域中事物的抽象,是软件系统中用来描述客观事物的一个实体。对象以3栏矩阵表示,如图2-2所示构成对象的基本要素如下。
·名称:即对象的名字,用来在问题领域中区分其他对象。
·属性:描述对象特征的存贮或数据结构,它表明了对象的一个状态。
·操作:即对象的行为,分为两类,一类是对象自身承受的操作,即操作结果修改了自身原有属性状态;另一类是施加于其他对象的操作,即将产生的输出结果作为消息发送的操作。
在面向对象开发方法中,软件系统被看成是由一个个对象组成的,正确识别系统中的对象是进行系统开发的前提,问题领域中的实体、概念和事件等都可以抽象为对象,如下所示。
图2-2 对象的结构
·外部实体:与系统交换信息的外部设备、相关子系统、操作员或用户等。
·信息结构:问题领域中的概念实体,如信号、报表、显示信息等。
·需要记忆的事件:系统执行过程中产生并需要记忆的事件,如单击鼠标,敲击键盘等。
·角色:与系统交互的人员所扮演的角色,如学生、教师、会计等。
·组织机构:有关机构,如公司、部门、小组等。
·地点或位置:用做系统环境或问题上下文的场所、位置,如客户地址、收件人地址。
·操作规程:如操作菜单、某种数据输入过程等。
例如,现将图2-3中所示现实世界中的3个图形a、b、c分别抽象为矩形对象、菱形对象和梯形对象,其中矩形对象a的属性包括4个顶点坐标、中心点坐标、短边边长、长边边长、周长、面积,菱形对象b的属性包括四个顶点坐标、中心点坐标、边长、锐角度数、钝角度数、周长、面积,梯形对象c的属性包括梯形类型、四个顶点坐标、各边边长、各角度数、周长、面积,得到这3个对象的具体结构如图2-4所示。
图2-3 现实世界图形事物
图2-4 软件系统图形对象
对象是对问题领域中事物的抽象,它具有如下特性。
·问题领域中的实体和概念都可以抽象为对象。例如在银行领域,对象包括银行账户、汇率、出纳员、支票等;在学校领域,对象包括学生、教师、课程、成绩单和教室等;在制造企业领域,对象包括企业员工、管理员、顾客、销售单、产品等。
·每个对象都是唯一的,对象的唯一性与现实世界中事物的唯一性相一致。
·对象具有属性和行为。例如某网上宠物销售系统中的注册用户的属性可以包括姓名、性别、籍贯、文化程度、E-Mail地址,行为可以有登录系统、浏览信息和在线购买等。
·对象具有状态。状态指某个瞬间对象的各个属性的取值,对象的某些行为往往会改变对象自身的状态,即属性的取值。例如某订单的状态,在用户刚下订单的时候为“待审核”,订单通过审核后为“正在配货”,库房发出商品至快递公司后为“订单处理结束”。
·对象都属于某个类。每个对象都是某个类的实例,例如,注册用户张三、李四和王五,他们都属于注册用户类。
3.类
类是具有相同属性和操作的一组对象的集合。构成类的基本要素与对象相同,如图2-5所示,类可以视为一个具有相同属性与共同行为的对象的模板,可用来产生对象,可以说,对象是类的一个实例。同一个类的所有对象具有相同属性,表明它们的属性含义相同,但是它们的状态不一定相同,也就是属性取值不同,类的所有实例具有相同的行为,也就是说它们具有一些相同的功能。
图2-5 类的结构
现根据图2-4中的矩形对象、菱形对象和梯形对象分别抽象出矩形类、菱形类和梯形类,具体结构如图2-6所示。
图2-6 软件系统图形类
类是对象的模板,对象是类的实例,从内容上看,类和对象的区别在于:类中的属性只有定义,而对象中的属性有具体的值;类中定义了操作的实现步骤及需要传递的参数名,而对象在调用类中定义的操作时要给出具体的参数值。
4.消息
图2-7 消息传递模型
在面向对象开发方法中,系统功能是由各种对象的协同工作共同完成的,而对象间的交互都是通过传递消息来完成。消息是一个对象与另一个对象的通信单元,是要求某个对象执行其所属类定义的某个操作的请求,如图2-7所示,是对象间消息传递的基本模型。
一个消息由3部分组成:接收消息的对象名、请求执行的操作名、操作中的参数。
消息的格式为:对象.操作(参数),例如要求三角形对象旋转30°的消息可表示为:三角形.旋转(30)。
5.封装性
对象间传递消息是为了调用对象的操作以完成系统功能,而对象的属性和操作都被封装(隐藏)在其所属类中,外界需要通过接口才可进行访问。
接口是类的对外的、可见的一组操作的集合,但是接口只包含操作的声明,而没有操作的实现。简单地说,可以认为接口是一个抽象的概念,它是外界访问对象的属性和操作的中介。例如,在计算机系统中,计算机的各种外部设备,如打印机、扫描仪、U盘等都是通过接口和主机进行通信的。
对象的封装性为软件系统带来了以下优点。
·简化了用户的操作,极大减少了出错的可能。比如电视机系统,尽管它本身的实现很复杂,但用户使用起来却非常简便,只要通过遥控器上的几个按钮就能享受电视机的服务,电视机的功能实现细节被藏在机壳里,没有必要向用户公开。
·有助于建立各个子系统之间的松耦合关系,提高系统的独立性。当某一个子系统的实现发生变化时,只要它的接口不变,就不会影响到其他子系统。
·提高了软件系统的可重用性。每个系统都是一个相对独立的整体,可以在多种环境中得到重用。例如可充电电池就是一个可重用的独立系统,在相机、电动剃须刀和电动玩具中都能发挥作用。
·降低了构建大型系统的风险。即使整个系统不成功,个别的独立子系统可能还是有价值的,例如当相机损坏时,它的可充电电池依然有用,可以安装到手电筒中。
6.继承
当某些类具有一些相同的属性和操作时,就可把这部分属性和行为抽象到一个新的类中,抽象出的新类称作父类,被抽象的类称作子类。例如,从图2-6中的矩形类、菱形类和梯形类中可以抽象出四边形类,其结构如图2-8所示。
子类可直接获得父类的属性和操作,而不必重新定义它们,父类和子类之间的这种关系就是继承。当一个子类只有一个父类时,继承称为单继承;当一个子类有多个父类时,继承称为多重继承。子类除了拥有父类的属性和操作,还可以再定义新的属性和操作,即所谓的扩展,同时子类还可对父类中操作的实现方式进行重新定义。在图2-9所示的继承和扩展关系中,普通手机类和娱乐手机类是手机类的子类,两者都继承了手机的打电话功能,且分别从不同角度进行了功能扩展:普通手机对打电话功能的实现方式进行了重新定义,增加了来电屏蔽、固话转接等功能;娱乐手机则另外定义了播放音乐、拍照等新功能。
图2-8 软件系统四边形类
图2-9 继承和扩展实例
需要注意的是,子类只需在父类基础上定义新增的属性和操作,以及父类已有的但在子类中需重新定义的操作,以此提高了系统的可重用性。在父类中定义的操作被子类继承后,可以表现出不同的行为,继承的这种特性称为多态性。如三角形类和矩形类都继承了多边形类的画图操作,但是这个操作在两个子类中的实现方式和操作结果完全不一样,一个是画三角形,另一个是画矩形。
7.抽象
抽象是一种由具体到抽象、由复杂到简洁的思维方式。在面向对象的开发过程中,抽象体现在如图2-10所示的几个方面。
图2-10 面向对象开发过程中的抽象
在抽象的过程中,需要注意以下两个问题。
1)从问题领域的客观事物到软件系统中对象的抽象。在建立对象模型时,问题领域中的事物被抽象为对象,事物往往具有多种多样的属性,但是对象的属性却应该根据事物所处的问题领域来确定。比如对于小白兔来说,如果问题领域是菜市场,那么需要重点关注小白兔对象的体重和价格;如果问题领域是动物研究所,那么主要关注小白兔对象的年龄、性别、健康状况及其五脏六腑的构造;如果问题领域是宠物市场,那么会关注小白兔对象的颜色、脾气、生活习性和价格等。
2)从子类到父类的抽象。当一些类具有相同的属性和操作时,可以把这部分属性和操作抽象为一个父类,这种从子类到父类的抽象分为两种情况。
·当不同子类具有实现方式一样的相同操作时,可将操作放在父类中实现,子类不必重复实现这个操作。例如自行车和三轮车的父类为非机动车类,两者都有刹车功能,并且实现方式也一样,在这种情况下,把刹车功能放在非机动车类中实现,子类不必重复实现这个功能,以此可以提高程序代码的可重用性和可维护性。
·当不同子类具有实现方式不一样的相同操作时,父类中仅声明此操作,由各子类分别实现这个操作。例如普通相机和数码相机都有拍照功能,但实现方式不一样,在这种情况下,它们的父类拍照设备类仅仅声明拍照功能,但不提供具体的实现步骤,这种抽象方式与面向对象的多态特性相结合,有助于提高子系统之间的松耦合性。
8.组合
在面向对象系统开发中,组合是一种利用简单子系统来组装出复杂系统的有效手段。如图2-11所示的个人计算机系统就是一个典型的组合系统,它由主机、键盘、鼠标、显示器和外部设备打印机等组成,而主机是由处理器、内存、一个或多个硬盘、网卡等组成。
面向对象开发方法中的组合具有以下优点。
·在软件分析和设计阶段,简化了复杂系统建立对象模型的过程。在建立对象模型时,通常先识别问题领域的初步对象,比如计算机,然后再对其进行分解,如分解为主机、键盘和显示器等,这符合人类从宏观到微观来认识世界的思维规律。
·在软件编程阶段,简化了创建复杂系统的过程,只需要分别创建独立的子系统,然后将它们组合起来,就构成了一个复杂系统,而且允许第三方参与系统的建设、提高了开发复杂系统的效率。
·向使用者隐藏系统的复杂性。尽管计算机内部的结构很复杂,但内部结构对用户是不透明的。
·提高了程序的可重用性。一个独立的子系统可以参与多个复杂系统的组合。
图2-11 计算机组合系统
2.2.3 面向对象开发方法的基本思路
面向对象开发方法的出发点和基本原则是尽可能模拟人类习惯的思维方式,使开发系统的方法与过程尽可能接近人类认识世界的方法与过程,也就是使描述问题的问题空间(也称为问题域)与实现解法的解空间(也称为求解域)在结构上尽可能一致。
在面向对象开发方法中,包括许多基本功能的系统被看成是由许许多多不同对象构成的,每一个对象都有自己的运动规律和内部状态,不同对象间的相互作用和通讯构成了完整的系统,如图2-12所示。例如,人类世界是由一个一个的人构成的系统,这个系统包括许多功能,如建筑、制造、营销、管理、交流等,这些功能是由人与人进行交互合作共同完成的。面向对象开发方法的核心思想是用对象及其之间的交互来解释系统,即系统的任何一个基本功能都是通过一些相关对象及其之间的交互来实现的,用这些对象及其之间的交互关系来对系统中每一个基本功能的完整实现过程进行描述,就能清楚地解释系统。
图2-12 面向对象软件系统结构模型
面向对象开发方法的基本思路。
1)客观世界是由各种对象组成的,任何事物都是对象,复杂的对象可以由比较简单的对象以某种方式组合而成。如人类社会由人、食品、建筑、机械产品、电子产品等对象组成,机械产品由各种零部件对象组成。同样,面向对象的软件系统也是由对象组成的,系统中的任何元素都是对象,复杂的系统对象由比较简单的系统对象组合而成。
2)所有对象被划分成各种对象类(Class,简称为类),每个类都定义了一组数据和方法。数据用于表示对象的静态属性,是对象的状态信息;类中定义的方法,是允许施加于该类对象上的操作,且方法是该类中所有对象共享的,并不需要为每个对象都复制操作的代码。如人类社会中的各种对象可以划分成人类、食品类、建筑类、机械产品类、电子产品类等,其中人类具有姓名、年龄、性别、职业、外貌特征等数据,同时能进行直立行走、思考、交流、工作等操作。
3)类之间存在父类与子类的派生关系,且按照父子关系形成一个具有层次结构的系统。由同一父类派生出来的子类都具有该父类的数据和方法,这种现象称为继承;若在子类中对某些数据或方法又做了重新描述,则在子类中这些数据或方法以新描述为准。如根据职业人类可派生出教师类、医生类、军人类、工人类、农民类等子类,它们除了继承人类共有的数据和方法,还具有与职业特征相关的其他数据和方法。
4)对象彼此之间仅能通过传递消息互相联系。对象是进行处理的主体,但它不能直接对它的私有数据进行操作,必须在有消息请求执行它的某个操作时,才能处理它的私有数据。一切属于某对象的私有信息,都被封装在该对象类的定义中,外界看不见更不能直接使用,这就是“封装性”。如人与人之间通过语言、肢体接触等方式进行交流,只有通过电视遥控接口才能使用电视等。
2.2.4 面向对象开发方法的开发步骤
面向对象开发方法在进行系统开发时一般要取得一组需求,用各种文字说明、图形、表格等工具形式或非形式地构造对象模型,识别与问题有关的类与类之间的联系,加上与解决方案直接有关的类(如界面),经对设计的类与联系进行调整后,对类进行编码及测试,得到结果。一般来讲采用面向对象开发方法开发系统主要分为3个阶段,即面向对象系统分析、面向对象系统设计和面向对象程序设计。
(1)面向对象系统分析
系统分析的任务是先确定系统要干什么,即对系统将要面临的具体管理问题以及用户的需求进行调查研究和分析,再在繁杂的问题领域中抽象地识别出对象,标识出对象间的关系,然后通过对对象的分析,确定对象属性及方法,利用属性变化规律完成对象及其关系的有关描述,并利用方法演变规律描述对象或其关系的处理流程。面向对象系统分析主要运用以下原则。
·构造和分解相结合的原则。构造是指由基本对象组装成复杂对象的过程,分解是对大粒度对象进行细化,从而完成系统模型细化的过程,这一原则是实现OOP的基础。
·抽象和具体结合的原则。抽象是指强调事务本质属性而忽略非本质细节,具体则是对必要的细节加以刻画的过程。面向对象方法中,抽象包括数据抽象和过程抽象:数据抽象把一组数据及有关操作封装起来;过程抽象则定义了对象间的相互作用。
·封装的原则。封装是指对象的各种独立外部特性与内部实现相分离,从而减少了程序间的相互依赖,有助于提高程序的可重用性。
·继承性的原则。继承是指直接获取父类已有的性质和特征而不必重复定义,这样,在系统开发中只需一次性说明各对象的共有属性和操作,对子类的对象只须定义其特有的属性和操作,继承的目的也是为了提高程序的可重用性。系统分析阶段得到的模型是具有一定层次关系的问题空间模型,这个模型相对有弹性,且易修改、易扩充。
(2)面向对象系统设计
一般而言,系统设计阶段就是将分析阶段的各层模型化的“问题空间”逐层扩展,得到一个模型化的特定“实现空间”,同时还要进行系统运行所需的软硬件、网络设计,以及应用系统等的设计,即给出系统实现的具体方案。
(3)面向对象程序设计
这一阶段主要是将OOD中得到的模型利用程序设计实现,具体操作包括选择程序设计语言编程、测试、试运行等。前面两阶段得到的对象及其关系最终都必须由程序语言、数据库等技术实现,但由于在设计阶段对此有所侧重考虑,故系统实施不会受具体编程语言的制约,因而本阶段占整个开发过程的比重较小。
2.2.5 面向对象开发方法的特点
面向对象开发方法把软件系统看成各种对象的集合,对象就是最小的子系统,一组相关的对象能够组合成更复杂的子系统。面向对象开发方法具有以下优点。
1)把软件系统看成是各种对象的集合,这更接近人类的自然思维方式,也解决了结构化开发方法中客观世界描述工具与软件系统结构的不一致性问题。
2)软件系统需求的变动往往是功能的变化,而功能的执行者——对象,一般不会有大的变化,这使得按照对象设计出来的系统比较稳定。例如医疗系统的功能是治疗疾病,不同疾病的治疗是由相关科室相互合作共同完成,疾病的种类会随着时间和环境而变化,而对象医疗科室一般不会变化,因此由科室构成的医疗系统会比较稳定。
3)对象包括属性(数据)和操作(方法),对象把数据及方法的具体实现方式一起封装起来,这使得方法和与之相关的数据不再分离,从而提高了每个子系统的相对独立性和系统的可维护性。
4)支持封装、抽象、继承和多态,提高了软件的可重用性、可维护性和可扩展性。
5)减少了从系统分析、设计到软件模块结构实现之间的多次转换映射的繁杂过程,大大减少了后续软件的开发量,缩短了开发周期。
面向对象开发方法能够用于各类信息系统的开发,但是它对系统分析阶段的要求很高,若缺乏整体的规划,则很容易造成结构不合理,各部分关系失调,且该方法需要一定的软件环境支持,对于初学者来说难以上手,因而更加适用于处理过程明确、规模相对较小的系统的开发。