第10章 面向对象设计

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第10章面向对象设计•面向对象设计过程与准则•体系结构模块及依赖性•系统分解•问题域部分的设计•人机交互部分的设计•任务管理部分的设计•数据管理部分的设计•对象设计10.1面向对象设计过程与准则•面向对象设计过程(1)建立系统环境模型。在设计的初始阶段,系统设计师用系统环境图对软件与外部实体交互的方式进行建模。下图给出了系统环境图的一般的结构。10.1面向对象设计过程与准则(2)设计系统体系结构。体系结构设计可以自底向上进行,如将关系紧密的对象组织成子系统或层;也可以自顶向下进行,尤其是使用设计模式或遗产系统时,会从子系统的划分入手。(3)对各个子系统进行设计。对于面向对象的系统,典型的子系统有问题域子系统、人机交互子系统和任务管理子系统。(4)对象设计及优化。对象设计以问题领域的对象设计为核心,其结果是一个详细的对象模型。对象设计过程包括使用模式设计对象、接口规格说明、对象模型重构、对象模型优化4组活动。10.1面向对象设计过程与准则•面向对象设计准则(1)模块化传统的面向过程方法中的模块通常是函数、过程及子程序等,而面向对象方法中的模块则是类、对象、接口、构件等。在面向过程的方法中,数据及在数据上的处理是分离的;而在面向对象方法中,数据及其上的处理是封装在一起的,具有更好的独立性,也能够更好地支持复用。10.1面向对象设计过程与准则(2)抽象面向对象方法不仅支持过程抽象,而且支持数据抽象。类实际上就是一种抽象数据类型。可以将类的抽象分为规格说明抽象及参数化抽象。类对外开放的公共接口构成了类的规格说明,即协议。这种接口规定了外部可以使用的服务,使用者无需知道这些服务的具体实现算法。通常将这类抽象称为规格说明抽象。参数化抽象是指当描述类的规格说明时并不具体指定所要操作的数据类型,而是将数据类型作为参数。10.1面向对象设计过程与准则(3)信息隐藏在面向对象方法中,信息隐藏通过对象的封装性实现。对于类的用户来说,属性的表示方法和操作的实现算法都应该是隐藏的。(4)弱耦合耦合是指一个软件结构内不同模块之间互连的紧密程度。在面向对象方法中,对象是最基本的模块,因此,耦合主要指不同对象之间相互关联的紧密程度。10.1面向对象设计过程与准则(5)强内聚•内聚衡量一个模块内各个元素彼此结合的紧密程度。在面向对象设计中存在以下3种内聚:(1)服务内聚:一个服务应该完成一个且仅完成一个功能。(2)类内聚:设计类的原则是,一个类应该只有一个用途,它的属性和服务应该是高内聚的。类的属性和服务应该全都是完成该类对象的任务所必需的,其中不包含无用的属性或服务。如果某个类有多个用途,通常应该把它分解成(3)一般—特殊内聚:设计出的一般—特殊结构,应该符合多数人的概念,更准确地说,这种结构应该是对相应的领域知识的正确抽取。10.1面向对象设计过程与准则(6)•软件重用是提高软件开发生产率和目标系统质量的重要途径。•重用基本上从设计阶段开始。重用有两方面的含义:一是尽量使用已有的类(包括开发环境提供的类库,及以往开发类似系统时创建的类),二是如果确实需要创建新类,则在设计这些新类的协议时,应该考虑将来的可重复使用性。体系结构设计描述了建立计算机系统所需的数据结构和程序构件。一个好的体系结构设计要求软件模块的分层及编程标准的执行。在面向对象软件中,常见的软件模块有类、接口、包和构件。在设计阶段我们往往关注类、接口和包,在实现阶段关注构件,而在部署阶段则关注构件的部署,也就是将构件部署在哪些结点上。10.2体系结构模块及依赖性1.类在面向对象的程序设计中,类和接口是程序的基本组成单元。一个典型程序需要界面类专门负责表示用户界面信息,需要数据库类负责与数据库进行交互,需要有业务逻辑类负责算法计算等。在计算机程序中,要设计和实现的所有类都具有唯一的名字,在不同的阶段或从不同的角度可以将它们称为设计类、实现类、系统类、应用类等。类及其依赖性2.继承依赖性依赖性管理中最棘手的问题是由于继承所引起的依赖性。继承是一种在父类和子类之间共享属性和行为的方式,所以运行时可以用一个子类对象代替其父类对象。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个子类对象是一种特殊的父类对象,它继承父类的所有特征,同时它又可以覆盖父类的方法,从而改变从父类继承的一些特征,并可以在子类中增加一些新的功能。这样,从客户的角度看,在继承树中为请求提供服务的特定对象不同,系统的运行行为可能会有所不同。类及其依赖性(1)多态继承根据为请求提供服务的对象不同可以得到不同的行为,这种现象称为多态。在运行时对类进行实例化,并调用与实例化对象相应的方法,称为动态绑定、后期绑定或运行时绑定。相应地,如果方法的调用是在编译时确定的,则称为是静态绑定、前期绑定或编译时绑定。多态并不是伴随着继承而出现。如果在子类中不覆盖父类中的任何方法,就不会产生多态行为。很明显,继承会带来类和方法之间的依赖性。继承带来的依赖性有编译时继承依赖性和运行时继承依赖性。类及其依赖性类及其依赖性①编译时继承依赖性右图所示的例子说明了一棵树中类之间的编译时依赖性。在这个例子中,B继承A,但没有覆盖A中的方法do1()。因此,B和A之间没有运行时继承依赖性。也就是说,由于编译时依赖性的存在,A中do1()方法的任何变化,都会被B在编译时(静态地)继承。一般来说,所有的继承都会引入编译时依赖性。依赖性是可传递的,也就是说,如果C依赖B,B依赖A,那么C也依赖A。②运行时继承依赖性下图举例说明了在一棵继承树中涉及客户对象访问类服务的运行时继承依赖性。图中类B的do1()方法是从父类A继承来的,因此Test与B没有运行时继承依赖性,只是一个静态依赖性,通过从Test到A的关联来表明。如果在doTest方法中调用的是do2()方法,或者在B中覆盖了A的do1()方法,则从Test到A和B就会存在运行时依赖性。类及其依赖性(2)无多态继承使用继承最简单的方式是子类不覆盖从父类继承来的方法,这样就不存在多态性继承问题。虽然无多态的继承有时并不是十分有用,但理解和管理起来是最容易的。(3)扩展继承和约束继承扩展继承是指子类继承父类的属性,并且提供额外属性来增强类定义。子类是父类的一种,如果子类覆盖了父类的方法,那么被覆盖的方法应该实现该方法的定义,并且能够在子类的语境中工作。当一个类覆盖了继承来的方法,并对一些继承来的功能进行了限制,这时就产生了约束继承。这时,子类不再是父类的一种。有时,限制会造成继承方法的完全禁止。当方法的实现是空时,就会发生这种情况。类及其依赖性3.交互依赖性交互依赖性也称为方法依赖性,是通过消息连接产生的。如下图所示。类及其依赖性图中,CActioner使用方法do1()来发送一条消息do3()给EEmployee,因此,do1()依赖于do3()。依赖性向上传递给所属的类,因此,CActioner依赖于EEmployee。类似地,EOutMessage的do2()调用EEmployee的方法do3(),因此,EOutMessage依赖于EEmployee。类及其依赖性1.接口在UML2.0中,接口是不可直接实例化的特性集合的声明,即其对象不能直接实例化,需要通过类来实现,实现接口的类需要实现接口中声明的方法。UML2.0对流行编程语言中的接口概念进行了扩展。接口中不仅可以声明操作,还可以声明属性。由于允许在接口中存在属性,因此,在接口之间或者接口和类之间可能会产生关联。用另一个接口或类作为属性的类型可以表示关联。接口及其依赖性在UML2.0中,可以通过关联实现从接口到类的导航。但在Java中是无法实现的,因为Java规定接口中的数据元素必须是常量。接口与抽象类有相似之处,抽象类是至少包含一个没有实现的方法的类,如果在一个抽象类中所有的方法都没有实现,则称其为纯抽象类,从这一点上,接口和纯抽象类似乎没有区别。但实际上,接口和抽象类还是有着本质的区别。在只支持单继承的语言中,一个类只能有一个直接父类,但是却可以实现多个接口。接口及其依赖性2.实现依赖性一个类可以实现多个接口,由类实现的接口集合称为该类的供给接口。在UML2.0中,将一个类和该类实现的接口之间的依赖性称为实现依赖性。右图所示为实现依赖性的UML符号,在箭头末端的类实现了箭头所指向的接口。从图中可以看到,Class1实现了Interface1接口和Interface2接口,而Class2只实现了Interface2接口。接口及其依赖性3.使用依赖性一个接口可以为其他类或接口提供服务,同时也可能需要其他接口的服务。一个接口所需要的其他接口所提供的服务称为这个类的需求接口。需求接口详细说明一个类或接口需要的服务,从而可以为其客户提供服务。在UML2.0中,通过类(接口)和它所需接口之间的依赖关系来说明需求接口,这称为使用依赖性。下图所示为使用依赖性的UML符号,在箭头尾部的类或接口使用在箭头头部的接口。Class1使用Interface1,Interface1使用Interface2。在Java语言中,不允许接口之间的使用,只允许接口间的扩展继承。接口及其依赖性Class1包含方法do1(),而do1()调用操作op1()。在静态代码中,并不清楚需求接口的哪个实现提供了所需的服务,可以是实现Interface1的任何一个类实例。当Class1的一个执行实例设置数据成员myInterface的值时,具体实例才能确定,从而可以引用具体类的一个具体对象。接口及其依赖性1.包包(package)又可称为层或子系统,是表示组织类的一种方式,用于划分应用程序的逻辑模型。包是高度相关的类的聚合,这些类本身是内聚的,但相对于其他聚合来说又是松散耦合的。包可以嵌套。外层包可以直接访问包括在它的嵌套包中的任何类。包还可以导入其他包,例如,在包A中导入了包B,这意味着包A或者包A的元素可以引用包B或者包B的元素。因此,虽然一个类只属于一个包,但是它可以被导入其他包。包的导入操作会引入包之间的依赖性以及它们的元素之间的依赖性。包及其依赖性下图为UML包的例子。一个包可以不暴露任何成员,也可以明确标明它所包含的成员,或者用符号“”来表示。图中,包B拥有类X,包C拥有包D,包E拥有包F,包F拥有类Y和类Z。包及其依赖性如果包A的一些成员在某种程度上引用了包B的某些成员(包A导入了包B的一些成员),这隐含着双重含义。•包B的变化可能会影响包A,通常需要对包A重新进行编译和测试。•包A只能和包B一起使用。包及其依赖性包及其依赖性2.包依赖性本质上,两个包之间的依赖性来自于两个包中类之间的依赖性。类之间的循环依赖性是个特别棘手的问题,好在大多数情况下可以通过重新设计避免循环依赖性。通过在上图中增加新包可以消除包之间的循环依赖性。方法为:在第1个例子中将包B依赖的包A的元素从包A中分离出来,组成包C,使得包B不再依赖包A,而是依赖包C;包及其依赖性在第2个例子中,将包F所依赖的包D中的元素从包D中分离出来,组成包G。消除循环依赖性后如下图所示。包及其依赖性在面向对象的软件工程环境中,面向对象技术已达到了类级复用,而构件级复用则是比类级复用更高一级的复用,它是对一组类的组合进行封装(当然,在某些情况下,一个构件可能只包含一个单独的类),并代表完成一个或多个功能的特定服务,也为用户提供了多个接口。一个构件可以是一个编译的类,可以是一组编译的类,也可以是其他独立的部署单元,如一个文本文件、一个图片、一个数据文件、一个脚本等。构件及其依赖性从软件复用的角度,构件是指在软件开发过程中可以重复使用的软件元素,这些软件元素包括程序代码、测试用例、设计文档、设计过程、需求分析文档、甚至领域知识。可复用的软件元素越大,我们称复用的粒度就越大。为了能够支持复用,软件构件应具有以下特性:(1)独立部署单元:一个构件是独立部署的,意味着它必须能与它所在的环境及其他构件完全分离。(2)作为第三方的组装单元:构件必须具备很好的内聚性,必须封装它的实现,并且只通过良好定义的接口与

1 / 82
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功