1、第10章 面向对象设计 第10章 面向对象设计 10.1 面向对象设计的准则面向对象设计的准则10.2 启发规则启发规则10.3 系统分解系统分解10.4 设计问题域子系统设计问题域子系统10.5 设计人机交互子系统设计人机交互子系统10.6 设计任务管理子系统设计任务管理子系统10.7 设计数据管理子系统设计数据管理子系统10.8 设计类中的服务设计类中的服务10.9 设计关联设计关联10.10 设计优化设计优化10.11 面向对象分析与设计实例面向对象分析与设计实例10.12 小结小结第10章 面向对象设计 10.1 面向对象设计的准则面向对象设计的准则所谓优秀设计,就是权衡了各种因素,从
2、而使得系统在其整个生命周期中的总开销最小的设计。对大多数软件系统而言,60%以上的软件费用都用于软件维护,因此,优秀软件设计的一个主要特点就是容易维护。10.1.1 模块化模块化面向对象软件开发模式,很自然地支持了把系统分解成模块的设计原理:对象就是模块。它是把数据结构和操作这些数据的方法紧密地结合在一起所构成的模块。第10章 面向对象设计 10.1.2 抽象抽象 面向对象方法不仅支持过程抽象,而且支持数据抽象。类实际上是一种抽象数据类型,它对外开放的公共接口构成了类的规格说明(即协议),这种接口规定了外界可以使用的合法操作符,利用这些操作符可以对类实例中包含的数据进行操作。使用者无须知道这些
3、操作符的实现算法和类中数据元素的具体表示方法,就可以通过这些操作符使用类中定义的数据。通常把这类抽象称为规格说明抽象。此外,某些面向对象的程序设计语言还支持参数化抽象。所谓参数化抽象,是指当描述类的规格说明时并不具体指定所要操作的数据类型,而是把数据类型作为参数。这使得类的抽象程度更高,应用范围更广,可重用性更高。第10章 面向对象设计 10.1.3 信息隐藏信息隐藏在面向对象方法中,信息隐藏通过对象的封装性来实现:类结构分离了接口与实现,从而支持了信息隐藏。对于类的用户来说,属性的表示方法和操作的实现算法都应该是隐藏的。10.1.4 弱耦合弱耦合耦合指一个软件结构内不同模块之间互连的紧密程度
4、。在面向对象方法中,对象是最基本的模块,因此,耦合主要指不同对象之间相互关联的紧密程度。弱耦合是优秀设计的一个重要标准,因为这有助于使得系统中某一部分的变化对其他部分的影响降到最低程度。在理想情况下,对某一部分的理解、测试或修改,无须涉及系统的其他部分。第10章 面向对象设计 如果一类对象过多地依赖其他类对象来完成自己的工作,则无疑给理解、测试或修改这个类带来很大困难,而且还将大大降低该类的可重用性和可移植性。显然,类之间的这种相互依赖关系是紧耦合的。当然,对象不可能是完全孤立的,当两个对象必须相互联系、相互依赖时,应该通过类的协议(即公共接口)实现耦合,而不应该依赖于类的具体实现细节。一般来
5、说,对象之间的耦合可分为交互耦合和继承耦合,下面分别讨论它们。第10章 面向对象设计 1交互耦合交互耦合如果对象之间的耦合通过消息连接来实现,则这种耦合就是交互耦合。为使交互耦合尽可能松散,应该遵守下述准则:尽量降低消息连接的复杂程度。应该尽量减少消息中包含的参数个数,降低参数的复杂程度。减少对象发送(或接收)的消息数。第10章 面向对象设计 2继承耦合继承耦合与交互耦合相反,应该提高继承耦合程度。继承是一般化类与特殊类之间耦合的一种形式。从本质上看,通过继承关系结合起来的基类和派生类,构成了系统中粒度更大的模块。因此,它们彼此之间应该结合得越紧密越好。为获得紧密的继承耦合,特殊类应该确实是对
6、它的一般化类的一种具体化,也就是说,它们之间在逻辑上应该存在“is-a”的关系。因此,如果一个派生类摒弃了其基类的许多属性,则它们之间是松耦合的。在设计时应该使特殊类尽量多继承并使用其一般化类的属性和服务,从而更紧密地耦合到其一般化类。第10章 面向对象设计 10.1.5 强内聚强内聚 内聚衡量一个模块内各个元素彼此结合的紧密程度。也可以把内聚定义为:设计中使用的一个构件内的各个元素,对完成一个定义明确的目的所做出的贡献程度。在设计时应该力求做到高内聚。在面向对象设计中存在下述三种内聚。1服务内聚服务内聚一个服务应该完成一个且仅完成一个功能。2类内聚类内聚设计类的原则是,一个类应该只有一个用途
7、,它的属性和服务应该是高内聚的。类的属性和服务应该全都是完成该类对象的任务所必需的,其中不包含无用的属性或服务。如果某个类有多个用途,通常应该把它分解成多个专用的类。第10章 面向对象设计 3一般一般特殊内聚特殊内聚 设计出的一般特殊内聚应该符合多数人的概念,更准确地说,这种结构应该是对相应的领域知识的正确抽取。例如,虽然表面看来飞机与汽车有相似的地方(都用发动机驱动,都有轮子),但是,如果把飞机和汽车都作为“机动车”类的子类,则明显违背了人们的常识,这样的一般特殊内聚是低内聚的。正确的作法是,设置一个抽象类“交通工具”,把飞机和机动车作为交通工具类的子类,而汽车又是机动车类的子类。一般来说,
8、紧密的继承耦合与高度的一般特殊内聚是一致的。第10章 面向对象设计 10.1.6 可重用可重用软件重用是提高软件开发生产率和目标系统质量的重要途径。重用基本上从设计阶段开始。重用有两方面的含义:一是尽量使用已有的类(包括开发环境提供的类库及以往开发类似系统时创建的类);二是如果确实需要创建新类,则在设计这些新类的协议时,应该考虑将来的可重复使用性。第10章 面向对象设计 10.2 启启 发发 规规 则则10.2.1 设计结果应该清晰易懂设计结果应该清晰易懂使设计结果清晰、易读、易懂,是提高软件可维护性和可重用性的重要措施。显然,人们不会重用那些他们不理解的设计。保证设计结果清晰易懂的主要因素如
9、下:(1)用词一致。应该使名字与它所代表的事物一致,而且应该尽量使用人们习惯的名字。不同类中相似服务的名字应该相同。(2)使用已有的协议。如果开发同一软件的其他设计人员已经建立了类的协议,或者在所使用的类库中已有相应的协议,则应该使用这些已有的协议。第10章 面向对象设计(3)减少消息模式的数目。如果已有标准的消息协议,设计人员应该遵守这些协议。如果确需自己建立消息协议,则应该尽量减少消息模式的数目。只要可能,就使消息具有一致的模式,以利于读者理解。(4)避免模糊的定义。一个类的用途应该是有限的,而且应该从类名较容易地推想出它的用途。10.2.2 一般一般特殊结构的深度应适当特殊结构的深度应适
10、当应该使类等级中包含的层次数适当。一般来说,在一个中等规模(大约包含100个类)的系统中,类等级层次数应保持为72。不应该仅仅从方便编码的角度出发随意创建派生类,应该使一般特殊结构与领域知识或常识保持一致。第10章 面向对象设计 10.2.3 设计简单的类设计简单的类应该尽量设计小而简单的类,以便于开发和管理。当类很大的时候,要记住它的所有服务是非常困难的。经验表明,如果一个类的定义不超过一页纸(或两屏),则使用这个类是比较容易的。为使类保持简单,应该注意以下几点:(1)避免包含过多的属性。属性过多通常表明这个类过分复杂,它所完成的功能可能太多了。(2)有明确的定义。为了使类的定义明确,分配给
11、每个类的任务应该简单,最好能用12个简单语句描述它的任务。(3)尽量简化对象之间的合作关系。如果需要多个对象协同配合才能做好一件事,则破坏了类的简明性和清晰性。第10章 面向对象设计(4)不要提供太多服务。一个类提供的服务过多,同样表明这个类过分复杂。典型地,一个类提供的公共服务不超过7个。在开发大型软件系统时,遵循上述启发规则也会带来另一个问题,即设计出大量较小的类,这同样也会带来一定复杂性。解决这个问题的办法是把系统中的类按逻辑分组,也就是划分“主题”。10.2.4 使用简单的协议使用简单的协议 一般来说,消息中的参数不要超过3个。当然,不超过3个的限制也不是绝对的。但是,经验表明,通过复
12、杂消息相互关联的对象是紧耦合的,对一个对象的修改往往导致其他对象的修改。第10章 面向对象设计 10.2.5 使用简单的服务使用简单的服务采用面向对象方法设计出来的类中的服务通常都很小,一般只有35行源程序语句,可以用仅含一个动词和一个宾语的简单句子描述它的功能。如果一个服务中包含了过多的源程序语句,或者语句嵌套层次太多,又或者使用了复杂的CASE语句,则应该仔细检查这个服务,设法分解或简化它。一般来说,应该尽量避免使用复杂的服务。如果需要在服务中使用CASE语句,通常应该考虑用一般特殊结构代替这个类的可能性。第10章 面向对象设计 10.2.6 把设计变动减至最小把设计变动减至最小通常,设计
13、的质量越高,设计结果保持不变的时间也越长。即使出现必须修改设计的情况,也应该使修改的范围尽可能小。理想的设计变动曲线如图10.1所示。在设计的早期阶段变动较大,随着时间的推移,设计方案日趋成熟,改动也就越来越小了。图10.1中的峰值与出现设计错误或发生非预期变动的情况相对应。峰值越高,表明设计质量越差,可重用性也越差。第10章 面向对象设计 图10.1 理想的设计变动曲线第10章 面向对象设计 10.3 系系 统统 分分 解解人类解决复杂问题时普遍采用的策略是“分而治之,各个击破”。同样,软件工程师在设计比较复杂的应用系统时普遍采用的策略,也是首先把系统分解成若干个比较小的部分,然后再分别设计
14、每个部分。这样做有利于降低设计的难度,有利于分工协作,也有利于维护人员对系统进行理解和维护。系统的主要组成部分称为子系统,通常根据所提供的功能来划分子系统。例如,编译系统可划分成词法分析、语法分析、中间代码生成、优化、目标代码生成和出错处理等子系统。一般来说,子系统的数目应该与系统规模基本匹配。第10章 面向对象设计 各个子系统之间应该具有尽可能简单、明确的接口。接口确定了交互形式和通过子系统边界的信息流,但是无须规定子系统内部的实现算法。因此,可以相对独立地设计各个子系统。在划分和设计子系统时,应该尽量减少子系统彼此间的依赖性。采用面向对象方法设计软件系统时,面向对象设计模型(即求解域的对象
15、模型)与面向对象分析模型(即问题域的对象模型)一样,也由主题、类与对象、结构、属性和服务五个层次组成。这五个层次一层比一层表示的细节更多,我们可以把这五个层次想象为整个模型的水平切片。此外,大多数系统的面向对象设计模型在逻辑上都由四大部分组成。这四第10章 面向对象设计 大部分对应于组成目标系统的四个子系统,它们分别是问题域子系统、人机交互子系统、任务管理子系统和数据管理子系统。当然,在不同的软件系统中,这四个子系统的重要程度和规模可能相差很大,规模过大的在设计过程中应该进一步划分成更小的子系统,规模过小的可合并在其他子系统中。某些领域的应用系统在逻辑上可能仅由3个(甚至少于3个)的子系统组成
16、。我们可以把面向对象设计模型的四大组成部分想象成整个模型的四个垂直切片。典型的面向对象设计模型可以用图10.2表示。第10章 面向对象设计 图10.2 典型的面向对象设计模型 第10章 面向对象设计 10.3.1 子系统之间的两种交互方式子系统之间的两种交互方式在软件系统中,子系统之间的交互有两种可能的方式,分别是客户供应商(Client-supplier)关系和平等伙伴(peer-to-peer)关系。1客户客户供应商关系供应商关系在这种关系中,作为“客户”的子系统调用作为“供应商”的子系统,后者完成某些服务工作并返回结果。使用这种交互方案,作为客户的子系统必须了解作为供应商的子系统的接口,
17、然而后者却无须了解前者的接口,因为任何交互行为都是由前者驱动的。第10章 面向对象设计 2平等伙伴关系平等伙伴关系在这种关系中,每个子系统都可能调用其他子系统,因此,每个子系统都必须了解其他子系统的接口。由于各个子系统需要相互了解对方的接口,因此这种组织系统的方案比起客户供应商方案来,子系统之间的交互更复杂,而且这种交互方式还可能存在通信环路,从而使系统难于理解,容易发生不易察觉的设计错误。总的来说,单向交互比双向交互更容易理解,也更容易设计和修改,因此应该尽量使用客户供应商关系。第10章 面向对象设计 10.3.2 组织系统的两种方案组织系统的两种方案把子系统组织成完整的系统时,有水平层次组
18、织和垂直块组织两种方案可供选择。1层次组织层次组织层次组织方案把软件系统组织成一个层次系统,每层是一个子系统,上层在下层的基础上建立,下层为实现上层功能提供必要的服务。每一层内所包含的对象,彼此间相互独立;而处于不同层次上的对象,彼此间往往有关联。实际上,在上、下层之间存在客户供应商关系。低层子系统提供服务,相当于供应商;上层子系统使用下层提供的服务,相当于客户。第10章 面向对象设计 层次结构又可进一步划分成两种模式:封闭式和开放式。所谓封闭式,就是每层子系统仅仅使用其直接下层提供的服务。由于一个层次的接口只影响与其紧邻的上一层,因此,这种工作模式降低了各层次之间的相互依赖性,更容易理解和修
19、改。在开放模式中,某层子系统可以使用处于其下面的任何一层子系统所提供的服务。这种工作模式的优点,是减少了需要在每层重新定义的服务数目,使得整个系统更高效、更紧凑。但是,开放模式的系统不符合信息隐藏原则,对任何一个子系统的修改都会影响处在更高层次的那些子系统。设计软件系统时到底采用哪种结构模式,需要权衡效率和模块独立性等多种因素,通盘考虑以后再做决定。第10章 面向对象设计 通常,在需求陈述中只描述了对系统顶层和底层的需求,顶层就是用户看到的目标系统,底层则是可以使用的资源。这两层往往差异很大,设计者必须设计一些中间层次,以减少不同层次之间的概念差异。第10章 面向对象设计 2块状组织块状组织块
20、状组织方案把软件系统垂直地分解成若干个相对独立的、弱耦合的子系统,一个子系统相当于一块,每块提供一种类型的服务。利用层次和块的各种可能的组合,可以成功地由多个子系统组成一个完整的软件系统。当混合使用层次结构和块状结构时,同一层次可以由若干块组成,而同一块也可以分为若干层。例如,图10.3表示一个应用系统的组织结构,这个应用系统采用了层次与块状的混合结构。第10章 面向对象设计 图10.3 典型应用系统的组织结构第10章 面向对象设计 10.3.3 设计系统的拓扑结构设计系统的拓扑结构完整系统的典型的拓扑结构有管道型、树形、星形等。设计者应该采用与问题结构相适应的、尽可能简单的拓扑结构,来减少子
21、系统之间的交互数量。第10章 面向对象设计 10.4 设计问题域子系统设计问题域子系统10.4.1 调整需求调整需求有两种情况会导致修改通过面向对象分析所确定的系统需求:一是用户需求或外部环境发生了变化;二是分析员对问题域理解不透彻或缺乏领域专家帮助,以致面向对象分析模型不能完整、准确地反映用户的真实需求。无论出现上述哪种情况,通常都只需简单地修改面向对象分析结果,然后再把这些修改反映到问题域子系统中。第10章 面向对象设计 10.4.2 重用已有的类重用已有的类代码重用从设计阶段开始,在研究面向对象分析结果时就应该寻找使用已有类的方法。若因为没有合适的类可以重用而确实需要创建新的类,则在设计
22、这些新类的协议时,必须考虑到将来的可重用性。如果有可能重用已有的类,则重用已有类的典型过程如下:选择有可能被重用的已有类,标出这些候选类中对本问题无用的属性和服务,尽量重用那些能使无用的属性和服务降到最低程度的类。在被重用的已有类和问题域类之间添加归纳关系(即从被重用的已有类派生出问题域类)。第10章 面向对象设计 标出问题域类中从已有类继承来的属性和服务,在问题域类内不需再定义它们了。修改与问题域类相关的关联,必要时将其改为与被重用的已有类相关的关联。10.4.3 把问题域类组合在一起把问题域类组合在一起在面向对象设计过程中,设计者往往通过引入一个根类而把问题域类组合在一起。事实上,这是在没
23、有更先进的组合机制可用时才采用的一种组合方法。此外,这样的根类还可以用来建立协议(见下一小节)。第10章 面向对象设计 10.4.4 增添一般化类以建立协议增添一般化类以建立协议在设计过程中常常发现,一些具体类需要有一个公共的协议。也就是说,它们都需要定义一组类似的服务(很可能还需要相应的属性)。在这种情况下可以引入一个附加类(例如根类),以便建立这个协议(即命名公共服务集合,这些服务在具体类中仔细定义)。第10章 面向对象设计 10.4.5 ATM系统之例系统之例图10.4描绘了第9章给出的ATM系统的问题域子系统的结构。在面向对象设计过程中,我们把ATM系统的问题域子系统进一步划分成了三个
24、更小的子系统,它们分别是ATM站子系统、中央计算机子系统和分行计算机子系统。它们的拓扑结构为星形,即以中央计算机为中心向外辐射,同所有ATM站及分行计算机通信。物理连接用专用电话线实现。根据ATM站号和分行代码,区分由每个ATM站和每台分行计算机连向中央计算机的电话线。第10章 面向对象设计 图10.4 ATM系统问题域子系统的结构第10章 面向对象设计 10.5 设计人机交互子系统设计人机交互子系统10.5.1 设计人机交互界面的准则设计人机交互界面的准则 遵循下列准则有助于设计出让用户满意的人机交互界面:(1)一致性。使用一致的术语、一致的步骤和一致的动作。(2)减少步骤。应使用户为做某件
25、事情而需敲击键盘的次数、点按鼠标的次数或者下拉菜单的距离都减至最少,还应使得技术水平不同的用户,为获得有意义的结果所需使用的时间都减至最少。特别应该为熟练用户提供简捷的操作方法(例如热键)。第10章 面向对象设计(3)及时提供反馈信息。每当用户等待系统完成一项工作时,系统都应该向用户提供有意义的、及时的反馈信息,以便用户能够知道系统目前已经完成该项工作的多大比例。(4)提供撤消命令。人在与系统交互的过程中难免会犯错误,因此,应该提供“撤消(undo)”命令,以便用户及时撤消错误动作,消除错误动作造成的后果。(5)无须记忆。不应该要求用户记住在某个窗口中显示的信息,然后再用到另一个窗口中,这是软
26、件系统的责任而不是用户的任务。此外,在设计人机交互部分时应该力求达到下述目标:用户在使用该系统时用于思考人机交互方法所花费的时间减至最少,而用于做其他实际想做的工作所用的时间达到最大值。更理想的情况是,人机交互界面能够增强用户的能力。第10章 面向对象设计(6)易学。人机交互界面应该易学易用,应该提供联机参考资料,以便用户在遇到困难时随时参阅。(7)富有吸引力。人机交互界面不仅应该方便、高效,还应该使人在使用时感到心情愉快,能够从中获得乐趣,从而吸引人去使用它。10.5.2 设计人机交互子系统的策略设计人机交互子系统的策略1分类用户分类用户人机交互界面是给用户使用的。显然,为设计好人机交互子系
27、统,设计者应该认真研究使用它的用户。应该深入到用户的工作现场,仔细观察用户是怎样做他们的工作的,这对设计好人机交互界面非常必要。第10章 面向对象设计 在深入现场的过程中,设计者应该认真思考下述问题:用户必须完成哪些工作?设计者能够提供什么工具来支持这些工作的完成?怎样使得这些工具使用起来更方便、更有效?为了更好地了解用户的需要与爱好,以便设计出符合用户需要的界面,设计者首先应该把将来可能与系统交互的用户分类。通常从下列几个不同角度进行分类:按技能水平分类(新手/初级/中级/高级)。按职务分类(总经理/经理/职员)。按所属集团分类(职员/顾客)。第10章 面向对象设计 2描述用户描述用户应该仔
28、细了解将来使用系统的每类用户的情况,把获得的下列各项信息记录下来。用户类型。使用系统欲达到的目的。特征(年龄、性别、受教育程度、使用系统的权限等)。关键的成功因素(需求、爱好、习惯等)。技能水平。完成本职工作的脚本。第10章 面向对象设计 3设计命令层次设计命令层次 设计命令层次的工作通常包含以下几项内容:(1)研究现有的人机交互含义和准则。现在,Windows已经成了微机上图形用户界面事实上的工业标准。所有Windows应用程序的基本外观及给用户的感受都是相同的(例如,每个程序至少有一个窗口,它由标题栏标识;程序中大多数功能可通过菜单选用;选中某些菜单项会弹出对话框,用户可通过它输入附加信息
29、)。Windows程序通常还遵守广大用户习以为常的许多约定(例如,File菜单的最后一个菜单项是Exit;在文件列表框中用鼠标单击某个表项,则相应的文件名变亮,若用鼠标双击则会打开该文件)。第10章 面向对象设计 设计图形用户界面时,应该保持与普通Windows应用程序界面相一致,并遵守广大用户习惯的约定,这样才会被用户接受和喜受。(2)确定初始的命令层次。所谓命令层次,实质上是用过程抽象机制组织起来的、可供选用的服务的表示形式。设计命令层次时,通常先从对服务的过程抽象着手,然后再进一步修改它们,以适合具体应用环境的需要。(3)精化命令层次。为进一步修改完善初始的命令层次,应该考虑下列一些因素
30、:次序:仔细选择每个服务的名字,并在命令层的每一部分内把服务排好次序。排序时或者把最常用的服务放在最前面,或者按照用户习惯的工作步骤排序。第10章 面向对象设计 整体部分关系:寻找在这些服务中存在的整体部分模式,这样做有助于在命令层中分组组织服务。宽度和深度:由于人的短期记忆能力有限,命令层次的宽度和深度都不应该过大。操作步骤:应该用尽量少的单击、拖动和击键组合来表达命令,而且应该为高级用户提供简捷的操作方法。4设计人机交互类设计人机交互类人机交互类与所使用的操作系统及编程语言密切相关。例如,在Windows环境下运行的Visual C+语言提供了MFC类库。设计人机交互类时,往往仅需从MFC
31、类库中选出一些适用的类,然后从这些类派生出符合自己需要的类即可。第10章 面向对象设计 10.6 设计任务管理子系统设计任务管理子系统10.6.1 分析并发性分析并发性通过面向对象分析建立起来的动态模型,是分析并发性的主要依据。如果两个对象彼此间不存在交互,或者它们同时接受事件,则这两个对象在本质上是并发的。通过检查各个对象的状态图及它们之间交换的事件,能够把若干个非并发的对象归并到一条控制线中。所谓控制线,是一条遍及状态图集合的路径,在这条路径上每次只有一个对象是活动的。在计算机系统中用任务(task)实现控制线,一般认为任务是进程(process)的别名。通常把多个任务的并发执行称为多任务
32、。第10章 面向对象设计 对于某些应用系统来说,通过划分任务,可以简化系统的设计及编码工作。不同的任务标识了必须同时发生的不同行为,这种并发行为既可以在不同的处理器上实现,也可以在单个处理器上利用多任务操作系统仿真实现(通常采用时间分片策略仿真多处理器环境)。10.6.2 设计任务管理子系统设计任务管理子系统常见的任务有事件驱动型任务、时钟驱动型任务、优先任务、关键任务和协调任务等。设计任务管理子系统包括确定各类任务并把任务分配给适当的硬件或软件去执行。第10章 面向对象设计 1确定事件驱动型任务确定事件驱动型任务某些任务是由事件驱动的,这类任务可能主要完成通信工作,例如与设备、屏幕窗口、其他
33、任务、子系统、另一个处理器或其他系统通信。事件通常是表明某些数据到达的信号。在系统运行时,这类任务的工作过程如下:任务处于睡眠状态(不消耗处理器时间),等待来自数据线或其他数据源的中断;一旦接收到中断就唤醒了该任务,接收数据并把数据放入内存缓冲区或其他目的地,通知需要知道这件事的对象,然后该任务又回到睡眠状态。第10章 面向对象设计 2确定时钟驱动型任务确定时钟驱动型任务 某些任务每隔一定时间间隔就被触发以执行某些处理。例如,某些设备需要周期性地获得数据;某些人机接口、子系统、任务、处理器或其他系统也可能需要周期性的通信。在这些场合往往需要使用时钟驱动型任务。时钟驱动型任务的工作过程如下:任务
34、设置了唤醒时间后进入睡眠状态;任务睡眠(不消耗处理器时间),等待来自系统的中断;一旦接收到这种中断,任务就被唤醒并做它的工作,通知有关的对象,然后该任务又回到睡眠状态。第10章 面向对象设计 3确定优先任务确定优先任务优先任务可以满足高优先级或低优先级的处理需求。高优先级。某些服务具有很高的优先级,为了在严格限定的时间内完成这种服务,可能需要把这类服务分离成独立的任务。低优先级。与高优先级相反,有些服务是低优先级的,属于低优先级处理(通常指那些背景处理)。设计时可能用额外的任务把这样的处理分离出来。第10章 面向对象设计 4确定关键任务确定关键任务关键任务是有关系统成功或失败的关键处理,这类处
35、理通常都有严格的可靠性要求。在设计过程中可能用额外的任务把这样的关键处理分离出来,以满足高可靠性处理的要求。对高可靠性处理应该精心设计和编码,并且应该严格测试。5确定协调任务确定协调任务当系统中存在三个以上任务时,就应该增加一个任务,用它作为协调任务。引入协调任务会增加系统的总开销(增加从一个任务到另一个任务的转换时间),但是引入协调任务有助于把不同任务之间的协调控制封装起来。使用状态转换矩阵可以比较方便地描述该类任务的行为。这类任务应该仅做协调工作,不要让它再承担其他服务工作。第10章 面向对象设计 6尽量减少任务数尽量减少任务数必须仔细分析和选择每个确实需要的任务。应该使系统中包含的任务数
36、尽量少。设计多任务系统的主要问题是,设计者常常为了自己处理时的方便而轻率地定义过多的任务。这样做加大了设计工作的技术复杂度,并使系统变得不易理解,从而也加大了系统维护的难度。7确定资源需求确定资源需求使用多处理器或固件主要是为了满足高性能的需求。设计者必须通过计算系统载荷(即每秒处理的业务数及处理一个业务所花费的时间),来估算所需要的CPU(或其他固件)的处理能力。第10章 面向对象设计 设计者应该综合考虑各种因素,以决定哪些子系统用硬件实现,哪些子系统用软件实现。下述两个因素可能是使用硬件实现某些子系统的主要原因:现有的硬件完全能满足某些方面的需求。例如,买一块浮点运算卡比用软件实现浮点运算
37、要容易得多。专用硬件比通用的CPU性能更高。例如,目前在信号处理系统中广泛使用固件实现快速付里叶变换。设计者在决定到底采用软件还是硬件的时候,必须综合权衡一致性、成本和性能等多种因素,还要考虑未来的可扩充性和可修改性。第10章 面向对象设计 10.7 设计数据管理子系统设计数据管理子系统10.7.1 选择数据存储管理模式选择数据存储管理模式 1文件管理系统文件管理系统文件管理系统是操作系统的一个组成部分,使用它长期保存数据具有成本低和简单等特点。但是,文件操作的级别低,为提供适当的抽象级别还必须编写额外的代码。此外,不同操作系统的文件管理系统往往有明显差异。2关系数据库管理系统关系数据库管理系
38、统关系数据库管理系统的理论基础是关系代数,它不仅理论基础坚实而且有下列一些主要优点:提供了各种最基本的数据管理功能,例如中断恢复、多用户共享、多应用共享、完整性和事务支持等。第10章 面向对象设计 为多种应用提供了一致的接口。标准化的语言(大多数商品化关系数据库管理系统都使用SQL语言)。但是,为了做到通用与一致,关系数据库管理系统通常都相当复杂,且有下述一些具体缺点,以致限制了这种系统的普遍使用。运行开销大,即使只完成简单的事务(例如只修改表中的一行),也需要较长的时间。不能满足高级应用的需求。关系数据库管理系统是为商务应用服务的,商务应用中数据量虽大但数据结构却比较简单。事实上,关系数据库
39、管理系统很难用在数据类型丰富或操作不标准的应用中。第10章 面向对象设计 与程序设计语言的连接不自然。SQL语言支持面向集合的操作,是一种非过程性语言。然而大多数程序设计语言本质上却是过程性的,每次只能处理一个记录。3面向对象数据库管理系统面向对象数据库管理系统 面向对象数据库管理系统是一种新技术,主要有两种设计途径:扩展的关系数据库管理系统和扩展的面向对象程序设计语言。扩展的关系数据库管理系统是在关系数据库的基础上,增加了抽象数据类型和继承机制,此外还增加了创建及管理类和对象的通用服务。第10章 面向对象设计 扩展的面向对象程序设计语言扩充了面向对象程序设计语言的语法和功能,增加了在数据库中
40、存储和管理对象的机制。开发人员可以用统一的面向对象观点进行设计,不再需要区分存储数据结构和程序数据结构(即生命期短暂的数据)。目前,大多数“对象”数据管理模式都采用“复制对象”的方法:先保留对象值,然后在需要时创建该对象的一个副本。扩展的面向对象程序设计语言则扩充了这种机制,它支持“永久对象”方法:准确存储对象(包括对象的内部标识在内),而不是仅仅存储对象值。使用这种方法,当从存储器中检索出一个对象的时候,它就完全等同于原先存在的那个对象。“永久对象”方法为在多用户环境下从对象服务器中共享对象奠定了基础。第10章 面向对象设计 10.7.2 设计数据管理子系统设计数据管理子系统1设计数据格式设
41、计数据格式设计数据格式的方法与所使用的数据存储管理模式密切相关。下面分别介绍适用于每种数据存储管理模式的设计方法。(1)文件系统。具体步骤是:定义第一范式表:列出每个类的属性表;把属性表规范成第一范式,从而得到第一范式表的定义。为每个第一范式表定义一个文件。测量性能和需要的存储容量。修改原设计的第一范式,以满足性能和存储需求。第10章 面向对象设计 必要时把归纳结构的属性压缩在单个文件中,以减少文件数量,同时把某些属性组合在一起,并用某种编码值表示这些属性,而不再分别使用独立的域表示每个属性。这样做可以减少所需要的存储空间,但是增加了处理时间。(2)关系数据库管理系统。具体步骤是:定义第三范式
42、表:列出每个类的属性表;把属性表规范成第三范式,从而得出第三范式表的定义。为每个第三范式表定义一个数据库表。测量性能和需要的存储容量。修改先前设计的第三范式,以满足性能和存储需求。第10章 面向对象设计(3)面向对象数据库管理系统。具体步骤是:扩展的关系数据库途径:使用与关系数据库管理系统相同的方法。扩展的面向对象程序设计语言途径:不需要规范化属性的步骤,因为数据库管理系统本身具有把对象值映射成存储值的功能。2设计相应的服务设计相应的服务如果某个类的对象需要存储起来,则应在这个类中增加一个属性和服务,用于完成存储对象自身的工作。应该把为此目的增加的属性和服务作为“隐含”的属性和服务,即无需在面
43、向对象设计模型的属性和服务层中显式地表示它们,仅需在关于类与对象的文档中描述它们。第10章 面向对象设计 这样设计之后,对象将知道怎样存储自己。用于“存储自己”的属性和服务,在问题域子系统和数据管理子系统之间构成一座必要的桥梁。利用多重继承机制,可以在某个适当的基类中定义这样的属性和服务,如果以后某个类的对象需要长期存储,该类就从基类中继承这样的属性和服务。下面介绍使用不同数据存储管理模式时的设计要点。(1)文件系统。被存储的对象需要知道打开哪个(些)文件,怎样把文件定位到正确的记录上,怎样检索出旧值(如果有的话),以及怎样用现有值更新它们。此外,还应该定义一个ObjectServer(对象服
44、务器)类,并创建它的实例。该类提供下列服务:第10章 面向对象设计 通知对象保存自身;检索已存储的对象(查找、读值、创建并初始化对象),以便把这些对象提供给其他子系统使用。注意,为提高性能应该批量处理访问文件的要求。(2)关系数据库管理系统。被存储的对象应该知道访问哪些数据库表,怎样访问所需要的行,怎样检索出旧值(如果有的话),以及怎样用现有值更新它们。此外,还应该定义一个ObjectServer类,并声明它的对象。该类提供下列服务:通知对象保存自身;检索已存储的对象(查找、读值、创建并初始化对象),以便由其他子系统使用这些对象。第10章 面向对象设计(3)面向对象数据库管理系统。扩展的关系数
45、据库途径:与使用关系数据库管理系统时方法相同。扩展的面向对象程序设计语言途径:无需增加服务,这种数据库管理系统已经给每个对象提供了“存储自己”的行为。只需给需要长期保存的对象加个标记,然后由面向对象数据库管理系统负责存储和恢复这类对象。第10章 面向对象设计 10.7.3 例子例子为具体说明数据管理子系统的设计方法,让我们再看看图10.4所示的ATM系统。从图10.4中可以看出,唯一的永久性数据存储在分行计算机中。由于必须保持数据的一致性和完整性,而且常常有多个并发事务同时访问这些数据,因此,采用成熟的商品化关系数据库管理系统存储数据。应该把每个事务作为一个不可分割的批操作来处理,由事务封锁账
46、户直到该事务结束为止。在这个例子中,需要存储的对象主要是账户类的对象。为了支持数据管理子系统的实现,账户类对象必须知道自己是怎样存储的,有两种方法可以达到这个目的。第10章 面向对象设计(1)每个对象自己保存自己。账户类对象在接到“存储自己”的通知后,知道怎样把自身存储起来(需要增加一个属性和一个服务来定义上述行为)。(2)由数据管理子系统负责存储对象。账户类对象在接到“存储自己”的通知后,知道应该向数据管理子系统发送什么消息,以便由数据管理子系统把它的状态保存起来,为此也需要增加属性和服务来定义上述行为。使用这种方法的优点是无需修改问题域子系统。如上一小节所述,应该定义一个数据管理类Obje
47、ctServer并声明它的对象。这个类提供下列服务:通知对象保存自身或保存需长期存储的对象的状态;检索已存储的对象并使之“复活”。第10章 面向对象设计 10.8 设计类中的服务设计类中的服务10.8.1 确定类中应有的服务确定类中应有的服务需要综合考虑对象模型、动态模型和功能模型,才能正确确定类中应有的服务。对象模型是进行对象设计的基本框架。但是,面向对象分析得出的对象模型,通常只在每个类中列出很少几个最核心的服务。设计者必须把动态模型中对象的行为以及功能模型中的数据处理转换成由适当的类所提供的服务。一张状态图描绘了一个对象的生命周期,图中的状态转换是执行对象服务的结果。对象的许多服务都与对
48、象接受的事件密切相关。事实上,事件就表现为消息,接受消息的对象必然有由消息选择符指定的服务,该服务改变对象状态第10章 面向对象设计(修改相应的属性值)并完成对象应做的动作。对象的动作既与事件有关,也与对象的状态有关。因此,完成服务的算法自然也和对象的状态有关。如果一个对象在不同状态可以接受同样事件,而且在不同状态接受到同样事件时其行为不同,则实现服务的算法中需要有一个依赖于状态的DO_ CASE型控制结构。功能模型指明了系统必须提供的服务。状态图中状态转换所触发的动作,在功能模型中有时可能扩展成一张数据流图。数据流图中的某些处理可能与对象提供的服务相对应。下列规则有助于确定操作的目标对象(即
49、应该在该对象所属的类中定义这个服务)。第10章 面向对象设计 如果某个处理的功能是从输入流中抽取一个值,则该输入流就是目标对象。如果某个处理具有类型相同的输入流和输出流,而且输出流实质上是输入流的另一种形式,则该输入/输出流就是目标对象。如果某个处理从多个输入流得出输出值,则该处理是输出类中定义的一个服务。如果某个处理把对输入流处理的结果输出给数据存储或动作对象,则该数据存储或动作对象就是目标对象。当一个处理涉及多个对象时,为确定把它作为哪个对象的服务,设计者必须判断哪个对象在这个处理中起主要作用。通常在起主要作用的对象类中定义这个服务。下面两条规则有助于确定处理的归属。第10章 面向对象设计
50、 如果处理影响或修改了一个对象,则最好把该处理与处理的目标(而不是触发者)联系在一起。考察处理涉及的对象类及这些类之间的关联,从中找出处于中心地位的类。如果其他类和关联围绕这个中心类构成星形结构,则这个中心类就是处理的目标。第10章 面向对象设计 10.8.2 设计实现服务的方法设计实现服务的方法在面向对象设计过程中还应该进一步设计实现服务的方法,主要应该完成以下几项工作。1设计实现服务的算法设计实现服务的算法设计实现服务的算法时应该考虑下列几个因素:(1)算法复杂度。通常选用复杂度较低(即效率较高)的算法,但也不要过分追求高效率,应以能满足用户需求为准。(2)容易理解与容易实现。容易理解与容