第六章问题域部分的设计一、什么是面向对象设计1、OOA与OOD的关系:1)面向对象的设计就是在OOA的基础上运用面向对象方法,主要解决与现实有滚得问题,目标是产生一个符合现实条件的OOD模型。与现实条件有关的因素有:图形用户系统、硬件、操作系统、网络、数据管理系统和编辑语言等。2)由于OOD以OOA模型为基础且OOA与OOD采用一致的表示方法,这使得从OOA到OOD不存在转换,只需做必要的修改和调整,或补充某些细节,并增加几个与现实关联的相独立部分即可。因此OOA与OOD之间不存在分析与设计的鸿沟,二者能紧密衔接,大大降低了从OOA与OOD的难度、工作量和出错频率。3)OOA主要针对问题域,识别有关的对象以及它们之间的关系,产生一个映射问题域,满足用户需求,独立于实现的OOA模型。OOD主要解决与实现有关的问题,基于OOA模型,针对具体的软、硬件条件(如机器、网络、OS、GUI、DBMS等)产生一个可实现的OOD模型。2、OOD模型和过程在OOA阶段只考虑问题域和系统操作责任,在OOD阶段要考虑与具体实现的问题。问题域部分需求模型基本模型辅助模型模型公约控制驱动部分构件及部署部分人机交互部分数据管理部分图6-1OOD模型从一个侧面观察OOD模型包括五个主要部分——一个核心部分加四个外围部分问题域部分、人机交换部分、控制驱动部分、数据管理部分、构建及部署部分从另一侧面观察OOD模型每个部分仍采用OOA的概念和表示方法,只是在辅助模型中要增加分别于描述构件模型和部署的构件图和部署图。OOD过程:设计OOD模型的五个部分问题域部分的设计、人机交互部分的设计、控制流管理部分的设计、数据管理部分的设计、构件部署设计。前4项不强调次序,每个部分均采用与OOA一致的概念、表示法及活动,但具有自己独特的策略。进行构件部署设计要在其前面四个部分完成后进行。二、问题域部分的设计对OOA结果按实现条件进行补充与调整就是问题域部分。进行问题域部分设计,要继续运用OOA的方法,包括概念、表示法及一部分策略。不但要根据实现条件进行OOD设计,而且由于需求变化或新发现了错误,也要对OOA的结果进行修改。本章的重点是对OOA结果进行补充与调整,要强调的是这部分工作主要不是细化,但OOA未完成的细节定义要在OOD完成。1、为复用类而增加结构2、提高性能3、增加一般类以建立共同协议4、按编程语言调整继承5、转化复杂关联决定关系的实现方式6、调整与完善属性7、构造及优化算法8、决定对象间的可访问性9、定义对象实例10、其他如下针对一些主要的情况讲述如何进行问题域的设计1、为复用类而增加结构如果在OOA识别和定义的类是本次开发中新定义的,而且没有可复用的资源,则需要进一步设计和编程。如果已存在一些可复用的类,而且这些类既有分析、设计时的定义,又有源程序,那么,复用这些类即可提高开发效率与质量。可复用的类可能只是与OOA模型中的类相似,而不是完全相同对二者进行修改。1)如果完全相同,就把可复用的类直接加到问题域,并用{复用}标记所复用的类。2)如果大于,就把可复用的类直接加到问题域,并用{复用}标记所复用的类,所需要的累再继承它。3)如果大于,就把可复用的类直接加到问题域,删除可复用类中的多余信息,并用{复用}标记所复用的类。4)如果相似,按如下方法处理;·把要复用的类加到问题域,标以“复用”。·划掉(或标出)不用的属性与服务。·建立从复用类到问题域原有的类之间的泛化关系。·由于问题域的类继承了“复用”类的特征,所以有些属性和服务不需要了应该把它们划掉。·考虑修改问题域原有类的结构和连接,必要时移到“复用”类。车辆(复用)符号厂商样式序号认证可复用的类车辆序号颜色样式出厂日期序号认证问题域中的类车辆符号厂商样式序号认证可复用的类图6-2问题域中例题2、提高性能1)调整对象的分布把需要频繁交换信息的对象,尽量地放在一台处理机上。2)增加保存中间结果的属性或类避免以后重复计算。3)提高或降低系统的并发度,可能要人为地增加或减少主动对象。4)合并通讯频繁的类5)用聚合关系描述复杂类如果一个所描述事物过于复杂,其操作也可能比较复杂,因为其中间可能要包多项工作内容。对这种情况的处理,可考虑用聚合关系描述复杂类。6)细化对象的分类如果一个类的概念范畴过于大,那么它所描述的对象的实际情况可能就有若干差异。解决的一个方法就是把类划分的更细一些,在原先较为一般的类之下定义一些针对不同具体情况的类。在每个特殊类中分别定义适合各自对象的操作。3、增加一般类以建立共同协议1)增加一个类,将所有具有相同操作和属性的类组织在一起,提供通用的协议。2)增加一般的类,提供局部通用的协议。3)对相似操作的处理。通过对特征标记做小的修改,以使他们相同,然后再把他们提升到一般类中。4、按编程语言调整继承由于在OOA强调如实地反映问题域,OOD考虑实现问题,所用语言不支持多继承,甚至不支持继承。1)对多继承的调整方法一:采用聚合把多继承转换为单继承因为聚合和泛化是不同的概念,这种方法并不是通用的(按定义)。在大多数情况下,需要考虑形成多继承的原因,将本来在特殊类中显式定义的信息离出来,作为部分对象,以原来的一般类作为整体对象。ABCABC图6-3多继承中的一个继承换为聚合示例图6-4所示的模型中有一个多继承,现假设编程语言不支持多继承,仅支持单继承。人员研究生教职工在职研究生人员身份研究生教职工11..2图6-4多继承示例图6-5采用聚合的方式把多继承转换为单位继承示例由于在图6-4所示的模型是按人员身份对一本类“人员”进行分类,并形成了其下的两个特殊类“研究生”和“教职工”,现在用身份作为一个类,依据它对原模型进行调整,如图6-5所示。图6-5用类“人员”创建对象的用意不变。创建类“研究生”的对象时,使用类“人员”和类“身份”以及自身的信息,类“教职工”也与此类似。创建“在职研究生”的对象时,使用类图中的四个类的信息。方法二:采用压平的方式采用这种方法,使得类“教职工”和“研究生”中的一些特征要在类的“在职研究生”中重复出现,导致信息冗余。人员教职工在职研究生研究生图6-6采用压平的方式把多继承转换为单位继承人员研究生在职研究生研究生信息教职工信息教职工图6-7采用压平和聚合的方式把继承转换为单位继承2)取消继承方法一:把继承结构展平研究生在职研究生教职工图6-8完全取消继承示例方法二:采用聚合的方法ABCABC图6-9采用聚合的方式取消继承3)对多态性的调整在继承结构中,具有相同名字的属性和操作,在不同的类中可以具有不同的类型和行为。这种在继承结构中对同一命令具有不同的含义的机制,就是继承的多态。如果编程的语言不支持多态,就需要把多态有关的属性和操作的名字分别赋不同的含义,也即明确把他们是为不同的东西,不但如此,往往还要按实际要求,重新考虑对象的分类,并对属性和操作进行调整。属性“边数”、“顶点坐标”和操作“绘图”不能被所有的特殊继承或不参加修改的继承,就说明它们只能适合多边形集合的一个子集,把这个子集定义为一个特殊的类“不规则多边形”,并把这些属性和操作下降到该特殊类中。这样类“正多边形”和“矩形”也不能继承那些不适合自己的属性和操作,而是要自己进行的定义。如图6-1所示。身份正多边形矩形多边形线条色线形边数顶点坐标绘图填充*顶点坐标X边型*顶点坐标*绘图正多边形矩形线条色线形填充多边形不规则多边形边数顶点坐标绘图顶点坐标绘图顶点坐标绘图*绘图图6-10多态性的调整示例5、转化复杂关联决定关系的实现方式(1)对复杂关联的转化1)把多元关联和N元关联转化为二元关联2)把多对多关联类转化为一对多关联............供货商卖方买方......供需合同............供货商............客户............客户1..*0..*110..*1..*图6-11把多对关联转化为一对关联在图6-11中类“供需合同”设立了两个属性“卖方”和“买方”在实例化后分别用于记录类“供货商”和“客户”的对象的标识。若不仅仅需要从类“供需合同”的对象访问其他对象,还存在着其他对象的访问。(2)关联的实现方式对关联进行调整后,要考虑关联的实现方式。A.聚合决定在整体类中指出部分类时,是用部分类直接作为整体类中的属性的数据类型,还是把部分类用作指针或对象标识的基类型,再用这样的指针或对象标示定义整体类的属性。如果是组合,最好用第1种方式,否则就需要在程序中保证整体对象与部分对象的生命周期的一致性。B.关联通常,通过在对象中设立指针或对象标识以指向或记录另一端的对象的方法,来实现关联。如果是单向关联,就在源端的类中设立属性,用来标记另一端的类将来创建的对象。如果是双向关联,就在两端类中各设立属性,用来标记对方将来创建的对象。如果关联中对方类的多重性是1,那么可在本方设立一个指向对方对象的指针,或设立一个记录对方对象引用的属性。如果对方类的多重性大于1,那么可在本方设立一个指向对方对象的指针集合或引用集合。若关联的某端有角色名,最好把其作为另一端类的属性名,以访问与角色名相邻的类。6、调整完备与属性按照语法:[可见性]属性名[‘:’类型][‘=’初始值]对属性的定义进行完善。每一个属性或者包含单个值,或者包含作为一个整体的密切相关的一组值。学生姓名学号籍贯............学生姓名学号............籍贯国家省县......验证......11图6-12对编辑语言不支持的属性类型进行调整的示例若要给出对属性的性质的约束,如“工龄60”或“0≤英语成绩≤100”等,也要看语言是否对其直接支持,否则要在算法上考虑如何实现。为了维护数据的完整性,必须要考虑需要一起更新的多个相关联的数据值。特别是,当基本的数据发生变化时,必须更新导出的属性。通过下列方法可以做到这一点:1)显式的代码因为每一个导出属性是根据一个或多个基本对象属性定义的,更新导出属性的一种方法是,在更新基本对象属性的操作中插入更新导出属性的代码。这种附加的代码将明确地更新依赖基本对象属性的导出属性,使得基本属性与导出属性的值同步。2)批处理性的重计算当基本数据以批处理的方式改变时,可能在所有的基本数值改变之后,再重新计算所有的导出属性的值。3)触发器凡是依赖基本属性的属性,都必须将它自己向基本属性注册。当基本属性的值被更新时,由专门设置的触发器更新导出属性的值。7、构造和优化算法对于需要设计的操作,要从如下几方面进行详细地定义:1)按照定义操作的格式:[可见性]操作名[‘(’参数列表‘)’][‘:’返回类型]完善操作的定义。2)从问题域的角度,根据其责任,考虑实现操作的算法,即对象是怎样提供操作的。3)若操作有前后置条件或不变式,考虑编程语言是否予以支持。若不支持,在操作的方法中要予以实现。4)建议进一步地分析特定类的对象相关的所有交互图,找出其所有与之相关的消息。一个对象所要响应的每个消息都要由该对象的操作处理,其中的一个操作也可能要使用其他操作。如果类拥有状态图,还可根据内部转换以及外部转换上的动作,设计算法的详细逻辑。可用自然语言或进行了一定结构化的自然语言描述算法,也可以使用程序框图或活动图描述算法。在算法中还要考虑对例外和特殊情况的处理。如考虑对输入错误、来自中间件或其它软硬件的错误的消息以及其它例外情况的处理。在系统较为复杂或需要处理大批量的数据的情况下,若系统在性能上有要求,就要对系统的体系结构和算法进行优化。8、决定对象间的可访问性从类A的对象到类B的对象有4种访问性(1)属性可见性:B是A的一个属性(关联、聚合)classA{…;Bb;…}(2)参数可见性:B的对象是A的一个方法的参数(依赖)A.amethod(Bb)//间接地找到一个对象,并赋给b(3)局部声明可见性:B的对象是在A的一个方法中声明的一个局部变量(依赖)classA::amethod{…;Bb;…}(4)全局可见性:B的对象在某种程度上全局可见(依赖)声明B的全局实例变量。对于后三种情况而言,从类A到类B间存在着依赖关系,在程序