10
面向
对象
设计
2009 BUPT TSEG,第十章 面向对象设计修佳鹏,软件工程模型与方法Models&Methods of Software Engineering,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,本章内容,10.1 面向对象设计综述10.2 模型的层次化10.3 面向对象设计原则10.4 设计用例实现方案,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.1 面向对象设计综述,面向对象的设计就是在OOA模型基础上运用面向对象方法进行系统设计,目标是产生一个符合具体实现条件的面向对象设计(OOD)模型。与实现条件有关的因素有:图形用户界面、硬件、操作系统、网络、数据库管理系统、编程语言和可复用的类库等。OOD的成果:以UML包图等表示的软件体系结构以交互图和类图表示的用例实现针对复杂对象的状态图用以描述流程化处理过程的活动图等,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,面向对象的设计过程,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.2 模型的层次化,10.2.1 用户界面层10.2.2 控制器/处理层10.2.3 业务/领域层10.2.4 持久化层10.2.5 系统层,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,层次化模型,用户界面类实现了系统的主要用户界面元素。业务/领域类实现与业务领域相关的概念,源于领域模型。控制器处理类作为完成用例任务的责任承担者,用于协调、控制其他类共同完成用例规定的功能或行为。持久类把永久存储、检索、更新和删除对象的能力封装起来,使底层的存储技术不暴露出来。系统类为应用提供操作系统相关的功能,通过把特定于操作系统的特性包装起来,使软件与操作系统分离,增加应用的可移植性。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.2.1 用户界面层,用户界面层指与用户进行交互的部分,包含应用程序中用户界面部分的代码。系统与用户的用户界面可以以多种形式出现:图形用户界面GUI命令行界面其他交互界面(语音等)尽量将用户界面层与系统的业务逻辑分离,专门处理系统与用户的交互。用户与系统的交互方式发生变化,系统的基本业务逻辑不需改变;系统业务逻辑变化,在交互内容不变的情况下,用户界面不需要进行改变。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.2.2 控制器/处理层,当用户通过用户界面使用系统时,用户界面类会产生系统事件传递给控制器类,后者负责该系统事件的处理。在系统事件的处理过程中,控制器类可能会调用领域类、系统类甚至其他的控制器类。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.2.3 业务/领域层,在面向对象分析阶段,已经识别出了问题域中重要的概念,该阶段关注的是概念的本质含义以及属性。在面向对象设计阶段,将会对这些概念增加操作,并进行必要的修改和调整,使之成为设计模型中业务/领域层中的类。这也是为什么说OOA和OOD采用一致的表示法,OOA和OOD之间不存在结构化方法中分析与设计的鸿沟,两者能够紧密衔接。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.2.4 持久化层,对象持久化:将对象状态永久保存到物理存储介质中。持久化层提供了存储、检索、更新和删除对象的基础结构。持久层封装对永久存储介质的访问,但其本身并不是永久存储机制。例如,持久层可能封装了对关系数据库的访问,但本身不是数据库,而是完成持久化功能的类的集合。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,为何引入持久化类,引入持久层的目的在于当数据存储机制或策略发生变化的时候,能减少维护工作。目前大部分系统都是采用数据库作为存储介质。但数据库肯定会改变,包括:数据库升级从一种数据库移动到另一种数据库数据模式变化,如增加字段、修改字段名称、改变字段类型等持久层将对数据库的操作类封装起来,提供专门数据管理功能,向业务/领域对象提供持久化服务,从而使数据库变化对业务领域的影响的范围局部化。无论持久存储策略如何变化,业务/领域类都不会受影响,从而增加了应用程序的可维护性、可扩展性和可移植性。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.2.5 系统层,系统层提供对操作系统和非面向对象资源的访问。系统类将操作系统提供的系统调用封装起来,生成系统访问类,例如Java语言中的文件流类库。上层业务逻辑直接访问系统类。而不直接访问系统调用。这样当程序需要在不同操作系统平台上进行移植时,只需要修改少数系统类就可以了。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3 面向对象设计原则,10.3.1单一职责原则SRP(Single Responsibility Principle)10.3.2开闭原则OCP(Open Closed Principle)10.3.3里氏替换原则LSP(Liskov Substitution Principle)10.3.4依赖倒置原则DIP(Dependency Inversion Principle)10.3.5接口隔离原则ISP(Interface Segregation Principle)10.3.6组合/聚合复用原则(Composite/Aggregation Reuse Principle,CARP)10.3.7迪米特法则(Law of Demeter,LoD),2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3.1单一职责原则SRP(Single Responsibility Principle),就一个类而言,应该仅有一个引起它变化的原因。职责定义为“变化的原因”。如果你能够想到多于一个的原因去改变一个类,那么这个类就具有多于一个的职责。类承担了多个职责,等于这些职责都耦合在了一起。一个职责的变化可能会影响这个类完成其他职责的能力。因此,在构造对象时,应该将对象的不同职责分离至两个或多个类中,确保引起该类变化的原因只有一个,从而提高类的内聚度。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,SRP举例,ComputationalGeometryApplication,GUI,GraphicalApplication,ComputationalGeometry Application,GUI,GraphicalApplication,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3.2开闭原则OCP(Open Closed Principle),软件实体(类、模块、函数等)应该是可以扩展、但是不可修改的。当需求改变时,可以对模块进行扩展,以满足需求的变化。对模块行为进行扩展时,不必改动客户端模块的源代码或者二进制代码。符合OCP原则的程序只通过增加代码来变化,而不是通过更改现有代码来变化。实现OCP的关键是使用抽象来识别不同类之间的共性和变化点,利用封装技术对变化点进行封装。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,OCP实例,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3.3里氏替换原则LSP(Liskov Substitution Principle),子类应当可以替换父类并出现在父类能够出现的任何地方。类A(客户类)调用类B(服务器类)的操作A,由于类C是类B的子类,实际运行时,类C的实例可以替换类B的实例,完成操作A的功能,而对于类A来讲,这种替换是透明的。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3.4依赖倒置原则DIP(Dependency Inversion Principle),a高层模块不应该依赖于低层模块。二者都应该依赖于抽象。b抽象不应该依赖于细节。细节应该依赖于抽象。DIP揭示启发式规则是:依赖于抽象,而不是具体的类。也就是说,程序中所有的依赖关系应该终止于抽象类或者接口。应用程序中所编写的大多数具体类都是不稳定的。通过把它们隐藏在抽象接口的后面,可以隔离它们的不稳定性。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,DIP举例,每个较高层次都为它所需要的服务声明一个抽象接口,较低的层次实现了这些抽象接口,每个高层类都通过该抽象接口使用下一层的服务,这样高层就不依赖于低层。低层反而依赖于在高层中声明的抽象服务接口。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,DIP举例,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3.5接口隔离原则ISP(Interface Segregation Principle),采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。如果一个服务器类为多个客户类提供不同的服务,那么,服务器类应该为每一个客户类创建特定的业务接口,而不要为所有客户类提供统一的业务接口,除非这些客户类请求的服务相同。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3.6组合/聚合复用原则(Composite/Aggregation Reuse Principle,CARP),在一个新对象里面使用一些已有对象,使之成为新对象的一部分;新对象通过向已有对象委托(delegate)一部分责任而达到复用已有对象的目的。面向对象设计中有两种实现复用的手段:一种是继承,一种是组合/聚合。CARP原则告诉我们:实现复用时应首先使用组合/聚合,其次才考虑继承。has-a:组合/聚合Is-a:继承,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,组合/聚合的优缺点,优点:新对象访问已有对象的唯一方法是通过已有对象的接口,因此,新对象无法知道已有对象内部实现细节,从而支持了对象封装性。由于新对象将大部分职责委托给已有对象完成,因此,新对象可以将焦点集中在一个任务上,从而遵循SRP原则。组合/聚合复用可以在运行时刻动态进行,新对象可以动态引用与已有对象接口相同的对象。作为复用手段可以应用到几乎任何环境中。缺点:当新对象已经形成后,需要将另外的已有对象扩充到该新对象中比较困难,只能重新采用组合/聚合方法生成另外的新对象实现扩充。采用组合/聚合方法实现复用时会产生大量的新对象,给对象管理带来困难。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,继承的优缺点,优点:子类容易实现。子类容易扩展。缺点:继承复用破坏了封装性。如果父类发生改变,那么子类的实现也不得不发生改变。从父类继承而来的实现是静态的,不可能在运行时刻发生改变,没有足够的灵活性。继承复用只能在一个具有相似语义类层次的上下文环境中使用。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.3.7迪米特法则(Law of Demeter,LoD),最少知识原则:一个对象应当尽可能少的了解其它对象。只与你直接的朋友们通信,不要跟“陌生人”说话。符合下列条件的对象即为朋友:当前对象本身(this);以参量形式传入到当前对象方法中的对象;当前对象的实例变量直接引用的对象;当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友;当前对象所创建的对象。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.4 设计用例实现方案,10.4.1 类职责分配模式10.4.2 类职责分配10.4.3 持久化层设计10.4.4 创建设计类图10.4.5 startUp用例,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,设计用例实现简介,面向对象分析阶段已经将业务/领域层中的类基本识别出来,但是其他层的类在进入设计阶段之前都还没有考虑。设计用例实现方案就是要找出所有这些层中各需要有哪些对象(类)来参与、协同工作,以实现用例的各个场景。在设计用例实现方案过程中,我们会往业务/领域层的类中增加属性、操作和方法,实现其从分析类到设计类的转变。UML协作图是描述对象如何协作以实现用例场景的有效工具。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.4.1 类职责分配模式,设计类的来源有两部分。一部分由领域模型中的概念类转换而来另一部分则是为实现而新增的一些类,如负责对象持久化的类、负责通信的类。每一个设计类对应的对象都有自己明确的职责。对象的职责分为两种类型:了解型(knowing)职责。细分为三类:对象要了解自己私有的封装数据;了解相关联的对象;了解能够派生或者计算的事物。行为型(doing)职责。细分为三类:对象自身要能执行一些行为,如创建一个对象或者进行计算;对象要能启动其他对象中的动作;对象要能控制或协调其他对象中的活动。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,面向对象的设计模式,对象的职责通过调用对象的方法来实现。将职责分配给一个对象还是多个对象,是分配给一个方法还是多个方法要受到职责粒度的影响。面向对象设计最关键的活动是正确地给对象分配职责,这直接关系到设计模型的质量。模式是面向对象软件的设计经验,是可重用的设计思想,它描述了在特定环境中反复出现的一类设计问题,并提供经过实践检验的解决这类问题的通用模式。模式定义了一组相互协作的类,包括类的职责和类之间的交互方式。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,模式的组成,模式名称:一个助记名,用一两个词描述模式的问题、解决方案和效果;问题:描述了何时使用模式。或者说模式的使用问题域;解决方案:描述了设计的组成部分、组成部分之间的相互关系及各自的职责和协作方式;效果:描述了模式应用的效果和使用模式应权衡的问题。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,信息专家模式,给对象分配职责的通用原则:将职责分配给拥有履行职责所必需信息的类即信息专家。换言之,对象处理自己拥有信息的事务。根据信息专家模式,应该找到拥有履行职责所必须的信息的类,选取类的方法:如果在设计模型中存在相关的类,先到设计模型中查看;如果在设计模型中不存在相关的类,则到领域模型中查看,试着应用或扩展领域模型,得出相应的设计类。职责的实现需要信息,而信息往往分布在不同的对象中,一个任务可能需要多个对象(信息专家)协作来完成。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,考试系统实例,在线考试系统中,某个类需要知道某份考卷选择题的总得分。那么谁应该负责获取考卷的选择题总得分?分析:要得到一份考卷的选择题得分,需要知道哪些信息呢?这份考卷有哪些选择题、以及各选择题的得分。从单选题或多选题类中获取学生提交的答案。从考题规格说明中获取该题的标准答案。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,部分领域模型,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,信息专家,考卷向设计模型中加入一个软件类,命名为TestPaper,并且给这个类分配获取选择题总得分的职责,这个职责通过取名为getScore的方法实现。考题向设计模型中增加一个名为Question(考题)的抽象类,并且给这个类分配获取某道题得分的职责,这个职责通过取名为getSubScore的抽象方法实现 考试规格说明向设计模型中增加一个名为QuestionSpecification(考题规格说明)的类,并且给这个类分配获取该道选择题标准答案的职责,这个职责通过取名为getAnswer的方法实现。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,类的设计与协作,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,创建者模式,谁应该负责产生类的实例?如果符合下面的一个或者多个条件,则可将创建类A实例的职责分配给类B(B创建A)。B聚合(aggregate)或包含(contain)对象A;B记录(record)对象A;B密切使用对象A;B拥有创建对象A所需要的初始化数据(B是创建对象A的信息专家)。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,创建者模式举例,在线考试系统中,哪个类负责产生考卷生成规则项的实例呢?寻找哪个类聚合或者包含了许多考卷生成规则项的实例。考虑部分领域模型,因为考卷生成规则聚合了考卷生成规则项对象,因此考卷生成规则类是创建考卷生成规则项实例的很好候选者。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,控制器模式,哪个非用户接口类应该负责处理一个输入系统事件(一个系统输入事件是由一个外部参与者产生的事件)?把接收或者处理系统事件的职责分配给这样一个类:它代表整个系统,称为外观(facade)控制器;它代表一个发生系统事件的用例场景,这个类通常命名为“控制器”,称为用例控制器或者会话控制器。在相同的用例场景中使用同一个控制器类处理所有的系统事件;一次会话是与一个参与者进行交谈的一个实例。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,使用控制器的指导原则,1当一个系统不具有“太多”的系统事件,或者用户接口不可能将事件消息重定向到其他控制器时,选择外观控制器是合适的。这时,外观控制器相当于一个应用的封面,隔离了用户接口和应用逻辑。2如果外观控制器由于职责过多而变得“臃肿”的时候,应该选择用例控制器。如果选择了用例控制器,那么每一个用例都有一个不同的控制类,而且只有一个,以便维护用例的状态。用例控制器可以实现有一定执行顺序的系统操作。3不论是外观控制器还是用例控制器,它们只是接收系统事件消息,并没有实现系统操作的职责,系统操作应该委托给领域对象处理。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,10.4.2 类职责分配,寻找对象职责的有效方法之一是绘制交互图。交互图体现了如何为对象分配职责。当一个对象接收了某条消息,就表明该对象具有处理该条消息的职责。针对特定场景中各个系统事件单独绘制交互图,用以说明系统接收到该事件后,由哪些对象互相协作、来共同完成对事件的处理,从而抽象出对象以及对象的职责。,2009 BUPT TSEG 北京邮电大学 通信软件工程中心,考试系统中的操作,