《C程序设计(自考4737)第10章.ppt》由会员分享,可在线阅读,更多相关《C程序设计(自考4737)第10章.ppt(32页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第第10章章面向对象专题讨论面向对象专题讨论本章先讨论在实际应用中需要考虑的几个专题:本章先讨论在实际应用中需要考虑的几个专题:如何发现对象如何发现对象如何发现对象的数据成员和成员函数如何发现对象的数据成员和成员函数如何定义数据成员和成员函数如何定义数据成员和成员函数如何发现基类和派生类结构如何发现基类和派生类结构如何考虑接口继承与实现继承如何考虑接口继承与实现继承最后给出一个设计实例。最后给出一个设计实例。本章的目的是加深对知识的理解并锻炼解决实际问题的能本章的目的是加深对知识的理解并锻炼解决实际问题的能力,希望通过本章的学习,能将所学知识正确地运用到实力,希望通过本章的学习,能将所学知识正
2、确地运用到实际中去。际中去。主要内容主要内容10.1过程抽象和数据抽象过程抽象和数据抽象10.2发现对象并建立对象层发现对象并建立对象层10.3定义数据成员和成员函数定义数据成员和成员函数10.4如何发现基类和派生类结构如何发现基类和派生类结构10.5接口继承与实现继承接口继承与实现继承10.6设计实例设计实例10.1过程抽象和数据抽象过程抽象和数据抽象抽象抽象(abstraction)是形成概念的必要手段,它是从许多是形成概念的必要手段,它是从许多事物中舍弃个别的、非本质性的特征,抽取共同及本质性事物中舍弃个别的、非本质性的特征,抽取共同及本质性的特征,例如。谈到猫,世上没有任何两只猫是完全
3、相同的特征,例如。谈到猫,世上没有任何两只猫是完全相同的,但是舍弃了每只猫相互之间的差异,把共同和本质性的,但是舍弃了每只猫相互之间的差异,把共同和本质性的特征抽取出来,就形成了的特征抽取出来,就形成了“猫猫”这个概念。这个概念。对于分析而言,抽象原则具有两方面的意义:对于分析而言,抽象原则具有两方面的意义:尽管尽管问题域中的事物很复杂,但分析员并不需要了解问题域中的事物很复杂,但分析员并不需要了解和描述它们的全部,只需要分析研究其中与系统目标有关和描述它们的全部,只需要分析研究其中与系统目标有关的事物及其本质性特征。对于那些与系统目标无关的特征的事物及其本质性特征。对于那些与系统目标无关的特
4、征和许多具体的细节,即使有所了解,也应该舍弃。和许多具体的细节,即使有所了解,也应该舍弃。通过通过舍弃个体事物在细节上的差异,抽取其共同特征舍弃个体事物在细节上的差异,抽取其共同特征而得到一批事物的抽象概念。而得到一批事物的抽象概念。OOA中的类就是这样得到中的类就是这样得到的。的。抽象是面向对象方法中使用最为广泛的原则,例如系统中抽象是面向对象方法中使用最为广泛的原则,例如系统中的对象是对现实世界中事物的抽象;类是对象的抽象;数的对象是对现实世界中事物的抽象;类是对象的抽象;数据成员是事物静态特征的抽象;成员函数是事物动态特征据成员是事物静态特征的抽象;成员函数是事物动态特征的抽象等。的抽象
5、等。在软件开发领域中,早在面向对象方法出现之前就已经开在软件开发领域中,早在面向对象方法出现之前就已经开始运用抽象的原则,主要是过程抽象和数据抽象。始运用抽象的原则,主要是过程抽象和数据抽象。过程抽象过程抽象是指任何一个完成确定功能的操作序列,其使用是指任何一个完成确定功能的操作序列,其使用者都可把它看做一个单一的实体,尽管实际上它可能是由者都可把它看做一个单一的实体,尽管实际上它可能是由一系列更低级的操作完成的。运用过程抽象,软件开发者一系列更低级的操作完成的。运用过程抽象,软件开发者可以把一个复杂的功能分解为一些子功能;如果子功能仍可以把一个复杂的功能分解为一些子功能;如果子功能仍然比较复
6、杂,则可以进一步分解。这使得开发者可以在不然比较复杂,则可以进一步分解。这使得开发者可以在不同的抽象层次上考虑问题,在较高的层次上思考时可以不同的抽象层次上考虑问题,在较高的层次上思考时可以不关心较低层次的实现细节,即只注意一个过程完成什么功关心较低层次的实现细节,即只注意一个过程完成什么功能,而不注意它是怎样完成的。能,而不注意它是怎样完成的。过程抽象过程抽象不是不是OOA的主的主要抽象形式,因为面向对象方法不允许超出对象的界限在要抽象形式,因为面向对象方法不允许超出对象的界限在全系统的范围内进行功能的描述。但是过程抽象对于在对全系统的范围内进行功能的描述。但是过程抽象对于在对象范围内组织对
7、象的成员函数是很有用的。象范围内组织对象的成员函数是很有用的。数据抽象数据抽象是根据施加于数据之上的操作来定义数据类型,是根据施加于数据之上的操作来定义数据类型,并限定数据的值只能由这些操作来修改和观察。并限定数据的值只能由这些操作来修改和观察。程序设计语言的类型定义就是抽象原则的运用(如根据整程序设计语言的类型定义就是抽象原则的运用(如根据整数集合及该集合所适应的共同四则运算等操作,抽象出整数集合及该集合所适应的共同四则运算等操作,抽象出整数类型)。数类型)。20世纪世纪70年代后期形成的抽象数据类型理论明确提出把数年代后期形成的抽象数据类型理论明确提出把数据及其操作结合为一个整体并实行信息
8、隐蔽,这是对数据据及其操作结合为一个整体并实行信息隐蔽,这是对数据抽象原则的进一步发展。抽象原则的进一步发展。10.2发现对象并建立对象层发现对象并建立对象层软件开发者将被开发的整个业务范围称作软件开发者将被开发的整个业务范围称作“问题域问题域”。可。可以按如下步骤考虑发现对象并建立对象层。以按如下步骤考虑发现对象并建立对象层。1.将问题域和系统责任作为出发点将问题域和系统责任作为出发点问题域和系统责任从不同的角度告诉分析员应该设立哪些对问题域和系统责任从不同的角度告诉分析员应该设立哪些对象,问题域侧重客观存在的事物与系统中对象的映射象,问题域侧重客观存在的事物与系统中对象的映射,系统系统责任
9、侧重于系统责任范围内的每一项职责都应落实到某些对责任侧重于系统责任范围内的每一项职责都应落实到某些对象来完成。两者有很大部分重合,但又不完全一致。象来完成。两者有很大部分重合,但又不完全一致。2.正确运用抽象原则正确运用抽象原则OOA使用对象映射问题域中的事物,但并不是对分析员见使用对象映射问题域中的事物,但并不是对分析员见到的任何东西都在系统中设立相应的对象。到的任何东西都在系统中设立相应的对象。OOA需要正确需要正确地运用抽象原则,即紧紧围绕系统责任这个目标去进行抽象。地运用抽象原则,即紧紧围绕系统责任这个目标去进行抽象。抽象意味着要有所取舍。取舍的准则是看被观察的事物及抽象意味着要有所取
10、舍。取舍的准则是看被观察的事物及其特征是否与当前的目标有关。中国古代其特征是否与当前的目标有关。中国古代“九方皋相马九方皋相马”和和“庖丁解牛庖丁解牛”的故事就含有这种哲理。九方皋应伯乐之荐,的故事就含有这种哲理。九方皋应伯乐之荐,为秦穆公寻得千里马,但在复命时却把马的公、母和颜色为秦穆公寻得千里马,但在复命时却把马的公、母和颜色都说错了。穆公对其能力表示怀疑。伯乐回答说:都说错了。穆公对其能力表示怀疑。伯乐回答说:“皋之皋之所观,天机也:得其精而忘其粗,得其内而忘其外所观,天机也:得其精而忘其粗,得其内而忘其外”。把。把马牵来一试,果然是一匹千里马。精于相马的高手把主要马牵来一试,果然是一匹
11、千里马。精于相马的高手把主要精力都集中于跟自己的目标有关的特征上,忽略了与目标精力都集中于跟自己的目标有关的特征上,忽略了与目标无关的特征。宋国庖丁解牛时则从另一个角度抽象,他的无关的特征。宋国庖丁解牛时则从另一个角度抽象,他的目标是看牛的皮、肉、筋、骨中哪里有缝隙可以下刀,他目标是看牛的皮、肉、筋、骨中哪里有缝隙可以下刀,他是是“目无全牛目无全牛”。有人对此二事做成一联:。有人对此二事做成一联:九方皋相马,不分牡、牝、骊、黄,心唯骏马特征;九方皋相马,不分牡、牝、骊、黄,心唯骏马特征;宋庖丁解牛,只见筋、骨、皮、肉,目无全牛形象。宋庖丁解牛,只见筋、骨、皮、肉,目无全牛形象。在在OOA中正确
12、地运用抽象原则,首先要舍弃那些与系中正确地运用抽象原则,首先要舍弃那些与系统责任无关的事物,只注意与系统责任有关的事物。其次,统责任无关的事物,只注意与系统责任有关的事物。其次,对于与系统责任有关的事物,也不是把它们的任何特征都对于与系统责任有关的事物,也不是把它们的任何特征都在相应的对象中表达出来,而要舍弃那些与系统责任无关在相应的对象中表达出来,而要舍弃那些与系统责任无关的特征。判断事物是否与系统责任有关的关键问题,一是的特征。判断事物是否与系统责任有关的关键问题,一是该事物是否为系统提供了一些有用的信息该事物是否为系统提供了一些有用的信息(是否需要系统是否需要系统为它保存和管理某些信息为
13、它保存和管理某些信息);二是它是否向系统提供了某;二是它是否向系统提供了某些服务些服务(是否需要系统描述它的某些行为是否需要系统描述它的某些行为)。正确进行抽象,还需要考虑一些更深入的问题,即应该把正确进行抽象,还需要考虑一些更深入的问题,即应该把问题域中的事物映射为什么对象,以及如何对这些对象进问题域中的事物映射为什么对象,以及如何对这些对象进行分类。类的设置也可以有不同的决策。例如开发一个图行分类。类的设置也可以有不同的决策。例如开发一个图书馆管理系统时,设立书馆管理系统时,设立“书书”这个类,同时把每一本书作这个类,同时把每一本书作为该类的一个对象。这样做是正确的,因为系统需要记住为该类
14、的一个对象。这样做是正确的,因为系统需要记住每一本书借给了哪个读者。但在开发一个书店的业务管理每一本书借给了哪个读者。但在开发一个书店的业务管理系统时,是否要把每一本书作为一个对象呢?实际上,在系统时,是否要把每一本书作为一个对象呢?实际上,在这个系统中,把同一版本的一种书从总体上看成一个对象这个系统中,把同一版本的一种书从总体上看成一个对象更合理。因为该系统只要把每一种书看成一项货物,记住更合理。因为该系统只要把每一种书看成一项货物,记住它的货源、单价、库存量等信息就够了,不需记录每一本它的货源、单价、库存量等信息就够了,不需记录每一本书的信息。再如,在一个系统中,可以把管理者和工人设书的信
15、息。再如,在一个系统中,可以把管理者和工人设置成不同的类,而在另一个系统中,也可以把所有的人员置成不同的类,而在另一个系统中,也可以把所有的人员对象都用同一个对象都用同一个“人员人员”类来定义。类来定义。这些例子表明,如何把问题域中的事物抽象为对象和类,这些例子表明,如何把问题域中的事物抽象为对象和类,可以有不同的选择。在不同的系统中,抽象的程度可以有可以有不同的选择。在不同的系统中,抽象的程度可以有所不同。好的抽象应能清晰而简练地表达问题域,并使系所不同。好的抽象应能清晰而简练地表达问题域,并使系统的开销较少,这需要分析员根据不同系统的具体情况恰统的开销较少,这需要分析员根据不同系统的具体情
16、况恰如其分地运用抽象原则。如其分地运用抽象原则。为了尽可能全面地发现系统所需要的对象,分析员应该把为了尽可能全面地发现系统所需要的对象,分析员应该把握握“先松后紧先松后紧”的原则,即首先考虑各种能启发自己发现的原则,即首先考虑各种能启发自己发现对象的因素,尽可能找出各种可能有用的候选对象,宁可对象的因素,尽可能找出各种可能有用的候选对象,宁可多余,不可遗漏。然后对发现的候选对象逐个进行严格审多余,不可遗漏。然后对发现的候选对象逐个进行严格审查,筛选掉那些不必要的对象,或者对它们进行适当的调查,筛选掉那些不必要的对象,或者对它们进行适当的调整与合并,使系统中对象和类的数量尽可能少。整与合并,使系
17、统中对象和类的数量尽可能少。3.寻找候选对象的基本方法寻找候选对象的基本方法寻找候选对象的基本方法的主要策略是从问题域、系统边寻找候选对象的基本方法的主要策略是从问题域、系统边界和系统责任三方面找出可能有的候选对象。界和系统责任三方面找出可能有的候选对象。考虑问题域可以启发分析人员发现对象的因素。考虑问题域可以启发分析人员发现对象的因素。这些这些因素包括人员、组织、物品、设备、事件、表格、结构等。因素包括人员、组织、物品、设备、事件、表格、结构等。例如,从汽车这个对象向上可以联想到例如,从汽车这个对象向上可以联想到“车辆车辆”,向下可,向下可以联想到以联想到“客车客车”和和“货车货车”,左右可
18、以联想到,左右可以联想到“摩托车摩托车”;考虑到整体和局部的关系,可以联想到;考虑到整体和局部的关系,可以联想到“车队车队”和和“发动机发动机”或或“轮胎轮胎”。考虑系统边界可以启发分析人员发现一些与系统边界考虑系统边界可以启发分析人员发现一些与系统边界以外的活动者进行交互并处理系统对外接口的对象。以外的活动者进行交互并处理系统对外接口的对象。考虑考虑的因素有人员、设备和外部系统。的因素有人员、设备和外部系统。考虑系统责任可以检查所存在的疏漏。考虑系统责任可以检查所存在的疏漏。通过对照系统通过对照系统责任所要求的每一项功能,查看是否可以由现有对象完成。责任所要求的每一项功能,查看是否可以由现有
19、对象完成。4.审查和筛选对象审查和筛选对象将所发现的对象进行审查和筛选,一般遵循如下原则:将所发现的对象进行审查和筛选,一般遵循如下原则:舍弃无用对象。舍弃无用对象。舍弃的标准一般是看对象是否提供了舍弃的标准一般是看对象是否提供了有用的数据成员和成员函数,也就是对象所表现的行为特有用的数据成员和成员函数,也就是对象所表现的行为特征是否与要求有关。征是否与要求有关。对象精简。对象精简。对于只有一个数据成员或只有一个成员函对于只有一个数据成员或只有一个成员函数的对象,应考虑合并到相关对象中描述这个对象的特征。数的对象,应考虑合并到相关对象中描述这个对象的特征。推迟到推迟到OOD考虑的对象。考虑的对
20、象。有些对象的特征在分析阶段有些对象的特征在分析阶段不需要描述,则放到不需要描述,则放到OOD阶段再考虑。阶段再考虑。5.异常情况的检查和调整异常情况的检查和调整还要对类的异常情况进行检查和调整。一般认为出现下述还要对类的异常情况进行检查和调整。一般认为出现下述情况都算异常情况,需要进行调整:情况都算异常情况,需要进行调整:类的数据成员或成员函数不适合该类的全部对象。类的数据成员或成员函数不适合该类的全部对象。数据成员或成员函数相同的类。数据成员或成员函数相同的类。数据成员或成员函数相似的类。数据成员或成员函数相似的类。对同一事物的重复描述。对同一事物的重复描述。经过这些步骤,就建立了类图的对
21、象层。其实,这些就是经过这些步骤,就建立了类图的对象层。其实,这些就是由对象名组成的一组对象图。由对象名组成的一组对象图。10.3定义数据成员和成员函数定义数据成员和成员函数为了发现对象的数据成员,首先应考虑借鉴以往的为了发现对象的数据成员,首先应考虑借鉴以往的OOA结果,看看相同或相似的问题域是否有已开发的结果,看看相同或相似的问题域是否有已开发的OOA模模型,尽可能复用其中同类对象数据成员的定义。然后重点型,尽可能复用其中同类对象数据成员的定义。然后重点研究当前的问题域和系统责任,针对本系统应该设置的每研究当前的问题域和系统责任,针对本系统应该设置的每一类对象,按照问题域的实际情况,以系统
22、责任为目标进一类对象,按照问题域的实际情况,以系统责任为目标进行正确地抽象,从而找出每一类对象应有的数据成员。行正确地抽象,从而找出每一类对象应有的数据成员。1.寻找数据成员的一般方法寻找数据成员的一般方法针对每个对象提出并回答以下问题,从而启发自己从各种针对每个对象提出并回答以下问题,从而启发自己从各种角度去发现对象的属性。下面是一般的考虑原则:角度去发现对象的属性。下面是一般的考虑原则:考虑按一般常识这个对象应该有哪些数据成员。考虑按一般常识这个对象应该有哪些数据成员。对象对象的某些数据成员往往是很直观的,按照一般常识就可以知的某些数据成员往往是很直观的,按照一般常识就可以知道它应该由哪些
23、数据来描述。例如人的姓名、职业、地址、道它应该由哪些数据来描述。例如人的姓名、职业、地址、电话号码等数据,都很容易想到。不过,按照一般常识发电话号码等数据,都很容易想到。不过,按照一般常识发现的数据成员有时未必真正有用,应该在审查时去掉那些现的数据成员有时未必真正有用,应该在审查时去掉那些无用的数据。无用的数据。在当前的问题域中,这个对象应该有哪些数据成员。在当前的问题域中,这个对象应该有哪些数据成员。只有认真地研究当前问题域才能得到对象的数据成员。例只有认真地研究当前问题域才能得到对象的数据成员。例如商品的条形码,平常人们并不注意它,而考虑超级市场如商品的条形码,平常人们并不注意它,而考虑超
24、级市场这类问题域时,则会发现它是必须设置的数据。这类问题域时,则会发现它是必须设置的数据。根据系统责任的要求,这个对象应具有哪些数据成员。根据系统责任的要求,这个对象应具有哪些数据成员。对象的有些数据成员,只有具体地考虑系统责任才能决定对象的有些数据成员,只有具体地考虑系统责任才能决定是否需要。如果系统做了修改,取消了这项功能,则与此是否需要。如果系统做了修改,取消了这项功能,则与此相关的数据肯定是要取消的。相关的数据肯定是要取消的。建立这个对象是为了保存和管理哪些信息。建立这个对象是为了保存和管理哪些信息。例如在商例如在商场管理系统中,建立场管理系统中,建立“商品商品”对象,是为了由系统保存
25、和对象,是为了由系统保存和管理有关商品的哪些信息?或者说,是为了向系统提供有管理有关商品的哪些信息?或者说,是为了向系统提供有关商品的哪些信息?通过这个问题而发现的信息应该由对关商品的哪些信息?通过这个问题而发现的信息应该由对象的数据成员来表示。象的数据成员来表示。对象为了在服务中实现其功能,需要增设哪些数据成对象为了在服务中实现其功能,需要增设哪些数据成员。员。例如实时监控系统的传感器对象,为了实现其定时采例如实时监控系统的传感器对象,为了实现其定时采集信号的功能,需要一个集信号的功能,需要一个“时间间隔时间间隔”数据,为了实现其数据,为了实现其报警功能,需要一个报警功能,需要一个“临界值临
26、界值”数据。数据。对象有哪些需要区别的状态,是否需要增加一个数据对象有哪些需要区别的状态,是否需要增加一个数据成员来区分这些状态。成员来区分这些状态。例如,设备在例如,设备在“关闭关闭”、“待命待命”和和“工作工作”等不同状态下系统中的行为是不同的,需要在等不同状态下系统中的行为是不同的,需要在“设备设备”对象中设立一个对象中设立一个“状态状态”数据成员,用它的不同数据成员,用它的不同数据值来表示实际设备的不同状态。数据值来表示实际设备的不同状态。用什么数据成员表示整体用什么数据成员表示整体-部分结构和实例连接。部分结构和实例连接。整体整体-部分结构和实例连接在部分结构和实例连接在OOA模型中
27、都有相应的符号(见模型中都有相应的符号(见第第1章),但也要在有关的对象中设立相应的数据成员。章),但也要在有关的对象中设立相应的数据成员。对于整体对于整体-部分结构,整体对象应有表明其部分对象的数部分结构,整体对象应有表明其部分对象的数据成员,可用嵌套对象或对象指针实现。据成员,可用嵌套对象或对象指针实现。上面列出的问题都是为了从各种不同的角度启发分析员发上面列出的问题都是为了从各种不同的角度启发分析员发现对象的数据成员。有些数据成员在不同的问题的启发下现对象的数据成员。有些数据成员在不同的问题的启发下都能得到。这种导致相同结果的重复思考并不是坏事,因都能得到。这种导致相同结果的重复思考并不
28、是坏事,因为目标是尽可能全面地发现数据成员,宁可多费点事也不为目标是尽可能全面地发现数据成员,宁可多费点事也不要遗漏。要遗漏。2.审查与筛选数据成员审查与筛选数据成员对于初步发现的数据成员,要进行审查和筛选。为此可对对于初步发现的数据成员,要进行审查和筛选。为此可对每个数据成员提出以下问题:每个数据成员提出以下问题:这个数据成员是否体现了以系统责任为目标的抽象。这个数据成员是否体现了以系统责任为目标的抽象。也就是说,该数据成员是否提供了系统中用得着的信息。也就是说,该数据成员是否提供了系统中用得着的信息。对现实世界中的事物如果脱离一定的目标去寻找它的特征对现实世界中的事物如果脱离一定的目标去寻
29、找它的特征可以找出很多,可以找出很多,OOA中应该只注意与系统责任有关的特中应该只注意与系统责任有关的特征。例如一本书有长、宽、高和重量,但是在图书馆管理征。例如一本书有长、宽、高和重量,但是在图书馆管理系统中,这些数据成员没有用就应该抛弃掉。系统中,这些数据成员没有用就应该抛弃掉。这个数据成员是否描述了这个对象本身的特征。这个数据成员是否描述了这个对象本身的特征。一个一个对象的数据成员应该描述这个对象本身的特征,否则即使对象的数据成员应该描述这个对象本身的特征,否则即使它在系统中提供了有用的信息,也不应该放置在这个对象它在系统中提供了有用的信息,也不应该放置在这个对象中。例如在教学管理系统中
30、,中。例如在教学管理系统中,“课程课程”这个对象,设这个对象,设“主主讲教师讲教师”这个数据成员是应该的,但是把教师的住宅、电这个数据成员是应该的,但是把教师的住宅、电话号码作为课程的数据成员就不合适了(尽管使用这个系话号码作为课程的数据成员就不合适了(尽管使用这个系统的教学管理人员可能感到这样做比较方便,他们可以从统的教学管理人员可能感到这样做比较方便,他们可以从“课程课程”对象得知如何跟教师联系)。在现实世界中,一对象得知如何跟教师联系)。在现实世界中,一门门课程不会有地址和电话号码。正确的做法应该把课程不会有地址和电话号码。正确的做法应该把“住址住址”和和“电话号码电话号码”作为作为“教
31、师教师”对象的数据成员。这样才能对象的数据成员。这样才能与问题域形成良好的对应,避免概念上的混乱,并避免因与问题域形成良好的对应,避免概念上的混乱,并避免因一个教师主讲多门课程而出现的信息冗余。一个教师主讲多门课程而出现的信息冗余。该属性是否破坏了对象特征的该属性是否破坏了对象特征的“原子性原子性”。认识事物认识事物的特征应该按照日常的思维习惯采用原子的(即不可分的)的特征应该按照日常的思维习惯采用原子的(即不可分的)概念。例如人的通信地址,包括国家、省市、街道、门牌概念。例如人的通信地址,包括国家、省市、街道、门牌号码和邮政编码等内容,但是这些内容在概念上是不可分号码和邮政编码等内容,但是这
32、些内容在概念上是不可分的。在定义的。在定义“人员人员”对象的数据成员时,应该使用一个对象的数据成员时,应该使用一个“通信地址通信地址”数据成员,而不应该把有关通信地址的各项数据成员,而不应该把有关通信地址的各项内容拆散开用多个数据成员来描述。这样,在对象中所定内容拆散开用多个数据成员来描述。这样,在对象中所定义的数据成员,既可能是简单的数据项(例如人的性别、义的数据成员,既可能是简单的数据项(例如人的性别、身份证号码等),也可能是由多个数据项组成的较为复杂身份证号码等),也可能是由多个数据项组成的较为复杂的数据结构(例如通信地址、出生年月等)。每一个数据的数据结构(例如通信地址、出生年月等)。
33、每一个数据成员都对应事物的一个原子特征。如果发现数据成员的设成员都对应事物的一个原子特征。如果发现数据成员的设置破坏了原子性,则应该加以修改。置破坏了原子性,则应该加以修改。这个数据成员是不是可以通过继承得到。这个数据成员是不是可以通过继承得到。如果当前对如果当前对象的类处于基类象的类处于基类-派生类结构的派生类位置上,则检查派生类结构的派生类位置上,则检查它的数据成员是不是可以通过继承来得到。凡是在基类中它的数据成员是不是可以通过继承来得到。凡是在基类中定义了的数据成员,都不要在派生类中重复出现。定义了的数据成员,都不要在派生类中重复出现。可以从其他数据成员直接导出的数据成员。可以从其他数据
34、成员直接导出的数据成员。如果一个如果一个数据成员的值明显可以从另外一个数据成员的值直接导出,数据成员的值明显可以从另外一个数据成员的值直接导出,则应该去掉。例如则应该去掉。例如“人员人员”对象有了对象有了“出生年月出生年月”数据数据成员,则成员,则“年龄年龄”数据不必再保留。不太明显的信息冗余,数据不必再保留。不太明显的信息冗余,即一个数据成员的值可以从其他很多数据成员的值经过比即一个数据成员的值可以从其他很多数据成员的值经过比较复杂的计算才能导出,则较复杂的计算才能导出,则OOA阶段暂不考虑其简化问阶段暂不考虑其简化问题,以保留题,以保留OOA模型的直观性。模型的直观性。3.定义成员函数定义
35、成员函数类的数据成员有静态和非静态两大类。静态是整个类的所类的数据成员有静态和非静态两大类。静态是整个类的所有对象所共有,而不是只属于一个对象。有对象所共有,而不是只属于一个对象。使用中要特别注意区分成员函数使用中要特别注意区分成员函数,非成员函数和友元函数非成员函数和友元函数三者。成员函数和非成员函数之间最大的差异是:成员函三者。成员函数和非成员函数之间最大的差异是:成员函数可以是虚函数,而非成员函数不可以是虚函数。虚函数数可以是虚函数,而非成员函数不可以是虚函数。虚函数必须是累的成员。友元函数不属于类,但在类中声明。必须是累的成员。友元函数不属于类,但在类中声明。C+系统会自动产生一些看不
36、到的默认成员函数,例如构系统会自动产生一些看不到的默认成员函数,例如构造函数、析构函数和赋值操作等等。但这些不一定符合造函数、析构函数和赋值操作等等。但这些不一定符合程序的要求,一定要为自己设计合适的成员函数,以免这程序的要求,一定要为自己设计合适的成员函数,以免这些默认函数达不到预期功能。些默认函数达不到预期功能。设计一个类时,要注意接口功能不仅要完整紧凑,而且要设计一个类时,要注意接口功能不仅要完整紧凑,而且要使类的结构达到最小化。使类的结构达到最小化。发现对象的成员函数的策略可以参考发现数据成员的做发现对象的成员函数的策略可以参考发现数据成员的做法,除了从系统责任和问题域方面考虑之外、还
37、要分析对法,除了从系统责任和问题域方面考虑之外、还要分析对象的状态及追踪服务的执行路线。一般要研究对象的状态象的状态及追踪服务的执行路线。一般要研究对象的状态与转换图。在考虑对象的行为分类时,一般从系统行为和与转换图。在考虑对象的行为分类时,一般从系统行为和对象自身的行为两方面考虑。对象自身的行为两方面考虑。最后还要检查每个成员函数是否真正有用,它们是否有很最后还要检查每个成员函数是否真正有用,它们是否有很强的内聚性,进一步调整它们的成员函数。将获得的数据强的内聚性,进一步调整它们的成员函数。将获得的数据成员和成员函数填入类图,就获得了类的特征层。成员和成员函数填入类图,就获得了类的特征层。1
38、0.4如何发现基类和派生类结构如何发现基类和派生类结构定义对象之后,需要研究类之间的关系。因篇幅所限,本定义对象之后,需要研究类之间的关系。因篇幅所限,本节仅简要介绍如何从不同的角度努力发现可用的基类和派节仅简要介绍如何从不同的角度努力发现可用的基类和派生类结构。生类结构。借鉴同类问题域以往的借鉴同类问题域以往的OOA结果,吸取其经验,发现可结果,吸取其经验,发现可复用的系统成分,对于复用的系统成分,对于OOA的每一个活动都是有益的,的每一个活动都是有益的,常常可以收到事半功倍的效果。常常可以收到事半功倍的效果。1.学习当前领域的分类学知识学习当前领域的分类学知识分类是一门学问,在许多行业和领
39、域已经形成了一套科学分类是一门学问,在许多行业和领域已经形成了一套科学的分类方法。例如植物分类学、动物分类学、图书分类法的分类方法。例如植物分类学、动物分类学、图书分类法等都成为一门学科;又如生产物资、日用商品、医药、化等都成为一门学科;又如生产物资、日用商品、医药、化学药品等也都有一套行之有效的分类方法。分析员应该花学药品等也都有一套行之有效的分类方法。分析员应该花一番功夫学习一点与当前问题域有关的分类学的知识,因一番功夫学习一点与当前问题域有关的分类学的知识,因为问题域现行的分类方(如果有)往往比较正确地反映了为问题域现行的分类方(如果有)往往比较正确地反映了事物的特征、类别以及各种概念的
40、一般性与特殊性事物的特征、类别以及各种概念的一般性与特殊性(恰好恰好对应对应C+的基类与派生类概念的基类与派生类概念)。学习这些知识,将对。学习这些知识,将对认识对象及其特征、定义对象的类、建立基类与派生类结认识对象及其特征、定义对象的类、建立基类与派生类结构有很大的帮助。按照本领域已有的分类方法,可以找出构有很大的帮助。按照本领域已有的分类方法,可以找出一些与它对应的基类与派生类。一些与它对应的基类与派生类。2.按照常识考虑事物的分类按照常识考虑事物的分类如果该问题域没有可供参考的现行分类方法,可以按照自如果该问题域没有可供参考的现行分类方法,可以按照自己的常识,从各种不同的角度考虑事物的分
41、类,从而发现己的常识,从各种不同的角度考虑事物的分类,从而发现其基类与派生类的关系。例如,对于其基类与派生类的关系。例如,对于“人员人员”可以从多种可以从多种角度去分类:青年人、成年人和老年人;男人和女人;黄角度去分类:青年人、成年人和老年人;男人和女人;黄种人、白种人和黑种人;干部与工人;在职人员与离退休种人、白种人和黑种人;干部与工人;在职人员与离退休人员;股东与职员;生产人员与销售人员等等。人员;股东与职员;生产人员与销售人员等等。从不同的角度考虑问题域中事物的分类,可以形成一些建从不同的角度考虑问题域中事物的分类,可以形成一些建立基类与派生类结构的初步设想,从而启发自己发现一些立基类与
42、派生类结构的初步设想,从而启发自己发现一些确实需要的基类与派生类结构。确实需要的基类与派生类结构。需要提醒的是,分类要考虑是否符合分类学常识。基类与需要提醒的是,分类要考虑是否符合分类学常识。基类与派生类结构中的各个类之间的关系应该符合分类学的常识派生类结构中的各个类之间的关系应该符合分类学的常识和人类的日常思维方式,如果违背了这些,就会产生一种和人类的日常思维方式,如果违背了这些,就会产生一种怪异的、有悖常理的怪异的、有悖常理的“结构结构”。例如,一个系统中先定义。例如,一个系统中先定义了了“汽车汽车”这个类,它有这个类,它有“发动机发动机”、“载重量载重量”、“运行运行速度速度”等数据成员
43、和等数据成员和“运输运输”等操作的成员函数。在定义等操作的成员函数。在定义“飞机飞机”这个类的时候,发现它也有这个类的时候,发现它也有“汽车汽车”的那些数据的那些数据与成员函数,只是增加了与成员函数,只是增加了“飞行高度飞行高度”等数据和等数据和“自动导自动导航航”等操作函数。于是就从等操作函数。于是就从“汽车汽车”类派生一个类派生一个“飞机飞机”类。这个结构是违背常理的。因为这等于说类。这个结构是违背常理的。因为这等于说“飞机是一种飞机是一种汽车汽车”,这样的结构违背了人类的常识,将使,这样的结构违背了人类的常识,将使OOA模型模型难于理解。造成这种问题的原因在于建立结构时只注意到难于理解。
44、造成这种问题的原因在于建立结构时只注意到继承,而没有注意到与问题域的实际事物之间分类关系的继承,而没有注意到与问题域的实际事物之间分类关系的对应。按分类学常识,它们可以都是对应。按分类学常识,它们可以都是“运输工具运输工具”的派生的派生类。类。3.回顾基类与派生类结构的两种定义回顾基类与派生类结构的两种定义按照基类与派生类结构的两种定义,可引导我们从两种不按照基类与派生类结构的两种定义,可引导我们从两种不同的思路去发现基类与派生类结构。一种思路是把每一个同的思路去发现基类与派生类结构。一种思路是把每一个类看做是一个对象集合,分析这些集合之间的包含关系。类看做是一个对象集合,分析这些集合之间的包
45、含关系。如果一个类是另外一个类的子集(例如如果一个类是另外一个类的子集(例如“职员职员”是是“人员人员”的子集,的子集,“轿车轿车”是是“汽车汽车”的子集),则它们应组织的子集),则它们应组织到同一个基类与派生类结构中。另一种思路是看一个类是到同一个基类与派生类结构中。另一种思路是看一个类是不是具有不是具有另一个类的全部特征。这包括两种情况:一是建立这些类另一个类的全部特征。这包括两种情况:一是建立这些类时已经计划让某个类继承另一个类的全部成员,现在应建时已经计划让某个类继承另一个类的全部成员,现在应建立基类与派生类结构来落实这种继承;另一种情况是起初立基类与派生类结构来落实这种继承;另一种情
46、况是起初只是孤立地建立每一个类,现在发现一个类中定义的成员只是孤立地建立每一个类,现在发现一个类中定义的成员(数据成员和成员函数数据成员和成员函数)全部在另一个类中重新出现,此事全部在另一个类中重新出现,此事应该考虑建立基类与派生类结构,把后者作为前者的派生应该考虑建立基类与派生类结构,把后者作为前者的派生类,以简化其定义。类,以简化其定义。以上两种思路最终结果是相同的,但是作为两种不同的手以上两种思路最终结果是相同的,但是作为两种不同的手段可以互为补充。段可以互为补充。4.考察类的成员考察类的成员对系统中的每个类,从以下两个方面考察它们的成员:一对系统中的每个类,从以下两个方面考察它们的成员
47、:一是看一个类的成员是否适合这个类的全部对象。如果某些是看一个类的成员是否适合这个类的全部对象。如果某些数据成员和成员函数只能适合该类的一部分对象,说明应数据成员和成员函数只能适合该类的一部分对象,说明应该从这个类中划分出一些派生类,建立基类与派生类关系。该从这个类中划分出一些派生类,建立基类与派生类关系。例如,例如,“公司人员公司人员”这个类,如果有这个类,如果有“股份股份”和和“工资工资”两个数据成员,通过审查可以发现,两个数据成员,通过审查可以发现,“股份股份”只能适应公只能适应公司的股东,司的股东,而而“工资工资”数据则只适合公司的职员。因此应在数据则只适合公司的职员。因此应在“公司人
48、公司人员员”类下建立类下建立“股东股东”和和“职员职员”两个派生类,并把两个派生类,并把“股股份份”和和“工资工资”分别放到这两个派生类中。这是一个分别放到这两个派生类中。这是一个“自自上而下上而下”的从基类到派生类并形成结构的策略。的从基类到派生类并形成结构的策略。另一方面检查是否有两个(或更多的)类含有一些共同的另一方面检查是否有两个(或更多的)类含有一些共同的数据成员和成员函数。如果有,则考虑若把这些共同的数数据成员和成员函数。如果有,则考虑若把这些共同的数据成员和成员函数提取出来,能否构成一个在概念上是包据成员和成员函数提取出来,能否构成一个在概念上是包含原来那些类的基类,组成一个基类
49、与派生类结构。例如含原来那些类的基类,组成一个基类与派生类结构。例如系统中原先分别定义了系统中原先分别定义了“股东股东”和和“职员职员”两个类,它们两个类,它们的的“姓名姓名”、“身份证号码身份证号码”等数据成员是相同的,提取等数据成员是相同的,提取这些数据成员可以构成一个这些数据成员可以构成一个“公司人员公司人员”类与类与“股东股东”及及“职员职员”组成基类与派生类结构。这是一个组成基类与派生类结构。这是一个“自下而上自下而上”的从基类到派生类并形成结构的策略。的从基类到派生类并形成结构的策略。还要对发现的结构进行审查、调整和简化,处理异常情况,还要对发现的结构进行审查、调整和简化,处理异常
50、情况,才能建立合适的结构。才能建立合适的结构。10.5接口继承与实现继承接口继承与实现继承设计一个类是给别人使用的,也就是通过成员函数提供与设计一个类是给别人使用的,也就是通过成员函数提供与外界打交道的接口。成员函数有实成员函数、虚成员函数外界打交道的接口。成员函数有实成员函数、虚成员函数和纯虚成员函数和纯虚成员函数(本节分别简称为实函数、虚函数和纯虚本节分别简称为实函数、虚函数和纯虚函数函数)3种,在实际中应该给用户提供哪些具体的成员函数种,在实际中应该给用户提供哪些具体的成员函数呢?现在假设设计一个可以供其他类继承的基类,派生类呢?现在假设设计一个可以供其他类继承的基类,派生类是用公有继承