第 9 章 面向对象设计

136
1 第9第 第第第第第第 • 面面面面面面面面面 •面 面面 • 面面面面 • 面面面面 • RUP 面面面面面 • RUP 面面面面面

Upload: chipo

Post on 12-Feb-2016

195 views

Category:

Documents


0 download

DESCRIPTION

第 9 章 面向对象设计. 面向对象的设计原则 系统设计 对象设计 设计模式 RUP 的设计活动 RUP 的实现活动. 9.1 面向对象的设计概念及原则. 1 、有关概念 面向对象设计将面向对象分析创建的分析模型变换为设计模型,它将作为软件构造的蓝图。但由于面向对象分析与设计活动是一个迭代与演化的过程,概念与表示方法的一致性使得分析与设计阶段平滑过渡。 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第 9 章  面向对象设计

1

第 9 章 面向对象设计• 面向对象的设计原则• 系统设计• 对象设计• 设计模式• RUP 的设计活动• RUP 的实现活动

Page 2: 第 9 章  面向对象设计

2

9.1 面向对象的设计概念及原则 1 、有关概念 面向对象设计将面向对象分析创建的分析模型变换为设计模型,它将作为软件构造的蓝图。但由于面向对象分析与设计活动是一个迭代与演化的过程,概念与表示方法的一致性使得分析与设计阶段平滑过渡。 传统的设计方法将问题域分解成一系列任务来完成,这些任务形成过程式软件的基本结构。面向对象方法把问题域作为一系列相互作用的对象,在此基础上构造出基于对象的软件系统结构。 面向对象设计包括系统设计和对象设计。系统设计包括如何把整个系统分解为子系统、子系统的软硬件布局等策略性决策;对象设计是根据具体的实现策略,对分析模型进行扩充。通过系统设计和对象设计产生设计模型,是进一步完成系统实现的基础。下表列出了分析模型与设计模型的区别:

Page 3: 第 9 章  面向对象设计

3

分析模型 设计模型概念模型,回避了实现问题; 物理模型,是实现蓝图 ;对设计是通用的; 针对特定的实现;对类型有 3 种构造型; 对类型有任意数量的构造型 (依赖于实现语言);不太形式化 ; 比较形式化;开发费用较低; 开发费用较高;层数少; 层数多;动态的; 动态的(特别关注时序);勾画系统的设计轮廓; 进行系统设计;主要通过研讨会等方式创建; 设计模型和实现模型需双向 开发;可能不需要在整个生命周期 在整个生命周期内都应该维护内都做维护

Page 4: 第 9 章  面向对象设计

4

2、 OO 设计原则 (1) 封装 是将一个完整的概念组成一个独立的单元,然后通过一个名字来引用它。在 OO 系统的较高层次,将一些相关的应用问题封装在一个子系统中,对子系统的访问是通过访问子系统的接口实现的;在较低的层次将具体对象的属性和操作封装在一个对象类中,通过类的接口访问其属性。 ( 2 )抽象 OO 方法不仅支持过程抽象还支持数据抽象。类封装了数据和操作数据的方法,类是一种包含过程抽象的数据抽象,它对外提供的公共数据接口构成了类的规格说明(类的协议)。使用者无需知道类中的具体操作是如何实现的,无需了解内部数据的具体表现方式,只要搞清它的规格说明,就可通过接口定义的操作访问类的数据。

Page 5: 第 9 章  面向对象设计

5

( 3 )信息隐蔽 信息隐蔽是通过对象的封装实现的。类的结构分离了接口和实现,对于类的使用者来说,属性的表示和操作的实现都是隐蔽的。 (4) 强内聚 • 服务内聚: 一个服务完成且仅完成一个功能。 • 类内聚: 一个类的属性和操作全部都是完成某个任务所必须的,其中不包括无用的属性和操作。 • 层内聚: 把向用户或高层提供相关服务的功能放在一起,而将其他内容排除在外。为了保证适当的层内聚,往往有严格的层次结构,高层能够访问低层的服务,而低层却不能访问高层的服务(下图描述了这种关系)。 以下的相关服务可以放在同一层:计算服务、消息或数据传输服务、数据存储服务、管理安全服务、用户交互服务、访问操作系统服务、硬件交互服务等。•

Page 6: 第 9 章  面向对象设计

6

用户界面

应用逻辑

访问操作系统 访问数据库 网络通信

应用程序的典型层次

处理应用协议

处理连接处理包

传输和接收

通信系统中的简化层次

Page 7: 第 9 章  面向对象设计

7

层向外界提供服务的过程和方法通常称为应用编程接口( Application Programming Interface, API)。 API的规格说明必须描述高层用来访问服务的协议,还要描述每个服务的语义和副作用。 层内聚的优点如下: • 替换高层模块对低层模块没有影响。 • 可以用等价的层替换低层,但必须复制该层所有的

API,这样高层才不受影响。 其他有关传统方法中的功能内聚、通信内聚、顺序内聚、时间内聚等概念及提高内聚的原则在 OO设计中仍然适用。

Page 8: 第 9 章  面向对象设计

8

( 5 )弱耦合 OO 设计中,耦合主要指不同对象(包括类、包)之间相互关联的程度,如果一个对象过多地依赖于其它对象来完成自己的工作,不仅使系统的可理解性下降,还会增加测试、修改的难度,同时降低了类的可复用性和可移植性。但对象不可能完全孤立,当两个对象必须相互联系时,只通过类的公共接口实现耦合,不应该依赖于类的具体实现细节。 设计时尽量减少对象之间发送的消息数( Meyer建议的少接口),减少消息中的参数个数(小接口),对象之间以明显和直接的方式通信,减少通信的复杂程度(显式的接口)。传统方法中有关降低耦合的原则在 OO 方法中仍然适用。

Page 9: 第 9 章  面向对象设计

9

( 6 )可复用 为了提高工作效率、减少错误、降低成本,就要充分考虑软件的复用性。复用有两个方面的含义:一是尽量使用已有的类,包括开发环境提供的类库和已有的相似类。二是创建新类时考虑将来的可复用性。 类有三种复用方式: • 实例复用 由于类的封装特性,使用者不需要了解内部的实现细节就可用适当的构造函数创建需要的实例,然后向所创建的实例发送适当的消息,启动相应的服务,完成需要的任务。 设计一个可复用性好的类是一件很困难的事情,因为,类提供的服务太多,会增加接口的复杂度,降低类的可理解性;提供的服务太少,则可能会降低复用性。在设计时,需要根据具体的应用环境和以往的经验来综合考虑,设计出合适的类构件。

Page 10: 第 9 章  面向对象设计

10

• 继承复用 当已有的类构件不能通过实例复用满足要求时,可以通过继承复用对已有的类构件进行修改,使它满足要求。在设计时,关键是要设计一个合理的、具有一定深度的类构件的继承层次结构。每个子类在继承父类的属性和服务的基础上,加入少量的新属性和新服务,这样做的好处是父子类的耦合度比较适当,接口简单,易于理解。 • 多态复用 多态是一种特性,这种特性使得一个属性或变量在不同的时期可以表示不同的对象。利用多态性可以使对象的对外接口更加一般化,系统运行时,根据接收消息的对象类型,由多态机制启动正确的方法,响应一个一般化的消息。

Page 11: 第 9 章  面向对象设计

11

例如出现下列情况: • 操作与数据结构大小有关。 • 操作与外部设备特性有关。 • 实现算法将来可能会改进。 为了克服与表示方法、数据结构或硬件特点相关的操作给复用带来的困难,可以设计一个基类把与上述操作有关的服务定义为纯虚函数,然后在复用时先派生出一个新类,在新类中重新定义上述操作的算法。 设计一个可复用的软件比设计一个普通软件的代价要高,但随着这些软件被复用的次数的次数的增加,分摊到它的设计和实现成本就会降低。

Page 12: 第 9 章  面向对象设计

12

( 7 )简洁化设计 一个软件 60%的工作量是维护工作。为了便于维护,现代软件工程越来越重视软件的简洁和易于理解。做好以下几点: • 设计简单的类。避免定义太多的属性和服务。一个类的职责要清晰,易于理解也有助于复用。 • 使用简单的协议。对象之间的关联是通过消息触发的,消息过于复杂,说明对象之间的耦合程度太紧,不利于维护。 • 设计结果简洁明了。设计结果(如文档)描述用词准确、清晰、容易理解。

Page 13: 第 9 章  面向对象设计

13

9.2 系统设计 和传统设计的目的相同,是为实现系统需求而对软件体系结构进行的设计。系统设计过程包括以下活动: • 划分分析模型为子系统。 • 标识问题的并发性。 • 选择软件体系结构的风格并分配子系统到处理器。 • 设计用户界面。 • 选择实现数据管理的基本策略。 • 标识全局资源及访问它们所需的控制机制。 • 为系统定义合适的控制流机制。 • 考虑边界条件。 • 评审并考虑权衡。

Page 14: 第 9 章  面向对象设计

14

1 、划分分析模型 以定义类、关系和行为的内聚集合,将这些设计元素包装为子系统。 定义子系统时应该遵循以下标准: • 子系统应该具有定义良好的接口,通过接口和系统的其余部分通信。 • “ ”除了少数的 通信类 ,在子系统中的类只和该子系统中的其他类协作。 • 子系统的数量不应太多。 • 子系统可以内部划分以降低复杂性。

Page 15: 第 9 章  面向对象设计

15

当两个子系统互相通信时,可建立客户 /服务器( C/S)结构或对等结构 (P2P)。在 C/S结构中,每个子系统只承担一个由客户端或服务器端隐含的角色,服务只是单向地从服务器端流向客户端;在 P2P结构中,服务可以双向流动。

在划分子系统时,往往进行分层设计。系统的每一层包含一个或多个子系统,表示了完成系统功能所需的功能性的不同抽象层次。抽象级别由与其相关的处理对用户的可见程度来确定。(如 PPt第 6页应用程序的典型层次结构)。 Buschmann及其同事提出以下分层设计方法: • 建立分层的标准。即决定子系统将如何被组合成层 次的体系结构。

Page 16: 第 9 章  面向对象设计

16

• 确定层的数量。太多使系统复杂,太少降低子系统的功能独立性。 • 命名层并将子系统分配到某个层。确信同层的子系统间的通信以及和其他层的子系统间的通信遵循软件体系结构设计思想。 • 定义每个层的接口。 • 精化子系统以建立每个层的类结构。 • 定义层间通信的消息模型。 • 评审层设计以保证层间的耦合度最小。 • 迭代以精化分层设计。

Page 17: 第 9 章  面向对象设计

17

2 、并发性与基于体系结构风格的子系统分配 当系统有许多并发行为时,要划分任务,简化并发行为的设计与编码。一个任务指系统中的一个过程,有时就是进程的同义词。并发任务可通过检查每个对象的状态图而定义,如果事件和变换流指明在任意时刻只有单个对象是活跃的,则是一个控制线程。即使一个对象向另一个对象发送消息,只要第一个对象等待响应,控制线程就继续。如果第一个对象不等待,则控制线程分叉。 OO 系统中的任务是通过孤立控制线程而设计的。例如当 SafeHome 系统正在监控其传感器时,它也可以拨号到监控站以检验连接。涉及这两个行为的对象(传感器、监控站)是同时活跃的,每个对象参与一个独立的控制线程并被定义为独立的任务。如果监控和拨号活动顺序地发生,则只设计单个任务。

Page 18: 第 9 章  面向对象设计

18

对象 - 行为模型对分析类间或子系统间的并发性提供了支持。如果类和子系统不是同时活动的,则不需要并发处理,它们可以实现在同一个处理器硬件上;如果类和子系统必须异步地或同时作用于事件,则被视为并发的。这时有两种选择:分配每个子系统到各自独立的处理器;分配子系统到同一处理器并通过操作系统特性提供并发支持。

分布式系统中, 软件体系结构风格对系统分布方案具有决定性的影响。选择体系结构风格应考虑以下因素:

Page 19: 第 9 章  面向对象设计

19

• 根据被开发系统的特点:如系统类型、用户需求、系统规模、使用方式等; • 网络协议:不同的网络协议支持不同的体系结构风格; • 可用的软件产品:包括网络软件、 OS、 DBMS 、现有的数据服务器等; • 成本及其他:购置硬件及软件成本、新开发软件成本、系统的安装与维护成本。此外,如开发人员对所选择体系结构风格下实现技术的熟练程度及开发期限等。 根据以上因素选择合适的体系结构,然后将子系统分配到体系结构的节点上。

Page 20: 第 9 章  面向对象设计

20

3 、任务管理设计 当一个节点上有多个控制流存在时,需要设计一个对这些控制流进行协调和管理的控制流(即进程或任务)。如设计一个进程,由它负责系统的启动和初始化、其他进程的创建与撤销、资源的分配、优先级的授予等工作。

用以下几步完成任务管理的设计: • 确定要执行的任务并识别它的特征。 • 确定任务的优先级。 • 创建协调任务来协调所有其他任务。 • 为每个任务设计对象,并定义它们之间的关系。 任务应该用模版详细描述,包括任务名、描述、优先级、服务、由谁管理、如何通信以及在层次中的位置,便于编程人员实现。

Page 21: 第 9 章  面向对象设计

21

4 、全局资源管理 全局资源包括物理资源(磁盘驱动器、处理器、通信线路)或逻辑资源(数据库、对象)。不但有访问权限的问题,还有访问冲突的问题。所以,应该标识全局资源,并制定访问它们的策略。一般的情况下,如果资源是物理对象,则通过建立协议实现并发系统的访问;如果资源是逻辑对象, Rumbaugh 建议对每个资源可创建一个“保护者”对象,控制对该资源的访问(鉴别身份、协调冲突)。

Page 22: 第 9 章  面向对象设计

22

5 、选择全局控制流机制 控制流是一个在处理机上顺序执行的动作序列。在分析过程中,一般不考虑控制流问题,因为假定所有的对象都能同时运行并在任何需要的时候就能执行它们的操作。系统设计的时候,就要考虑不是每个对象都能奢侈到在自己的处理器上运行。有 3 种可能的控制流机制: • 过程驱动控制: 控制来自程序代码中,如程序等待输入。这种控制流大多用于遗留系统并且使用过程化语言编写。当使用面向对象语言,操作的先后顺序分散在许多对象中,通过观察代码来决定输入的顺序将很困难。 • 事件驱动控制: 主循环等待外部事件,一旦事件到达就把与事件相关的信息分配给适当的对象。缺陷是错误过程会阻塞整个应用。

Page 23: 第 9 章  面向对象设计

23

• 线程 : 系统可以创建任意数量个线程,每个线程对应于不同的事件。如果某个线程需要更多的数据,就等待来自操作者的输入。这种控制流机制最直接,但需要比较成熟的支持线程的开发工具,特别是调试和测试工具。 一旦选定了控制流机制,就可用一组控制对象来实现它。控制对象的职责就是记录外部事件,存储它们的临时状态,并给出与外部事件相关的边界对象和实体对象的正确的操作次序。 6、数据管理设计 如何存储那些连续的、需要经常重新计算的对象?选择什么样的存储管理模式?

Page 24: 第 9 章  面向对象设计

24

3 种存储管理机制: ( 1)普通文件 由操作系统提供的存储机制,数据按字节流存储,数据操纵功能简单,适合于存储大容量的图形、图像、视频、音频等多媒体数据。数据存储时,每个类对应于一个文件,每个对象实例对应文件的一个纪录。

对象 对象 … 对象应用系统

数据接口

文件

用文件存储对象

记录 1记录 2

记录 n

Page 25: 第 9 章  面向对象设计

25

数据接口部分如何设计?主要设计为其他对象提供基本保存与恢复功能的对象类。对象存取器类 - 文件对照表 对象保存()对象恢复()

换算型对象存取器* 对象保存() * 对象恢复 ()

索引表文件记录索引 查找记录指针 ()

查找型对象存取器* 对象保存() * 对象恢复 ()

索引型对象存取器* 对象保存() * 对象恢复 ()文件系统数据接口的设计

从关键字换算出记录位置再保存或存取

以某种快速算法查找与关键字相符的记录用于某些类的对象实例不便于按关键字的值排序

Page 26: 第 9 章  面向对象设计

26

问题域部分的对象通过请求数据接口部分提供的服务实现对象的存取,这些永久性对象需要增加一些属性(如类名、关键字)和请求保存与恢复的操作,往往需定义一个在较高层次上的作为存储协议的抽象类。因此对原有的对象模型要作一些修改与调整。 ( 2 )关系数据库 提供了对数据存取、数据共享、数据完整性维护、故障恢复、事务处理等功能实现的数据操纵语言。数据以表的形式存储,表的每一列标识一个属性,每行把一个数据项标识成一个属性值的元组。不同表中的多个元组用来表示单个对象的属性。关系型数据库技术成熟,适合于大的数据集以及对属性数据的复杂查询。但关系数据库不适合储存多媒体数据和经过压缩处理过的数据(因为要求至少满足第一范式—每个属性必须是原子的,不再含有内部结构)

Page 27: 第 9 章  面向对象设计

27

对象 对象 … 对象应用系统

数据接口

RDBMS

表1表2

… 表 n

关系数据库

用关系数据库存储对象

由于关系数据库要求存入其中的数据符合一定的规范,因此需要对永久类的数据进行规范化(要花费代价),以消除关系中的函数依赖所带来的数据更新异常并减少数据冗余。 为了给数据查询、更新等操作带来方便,需要对永久类确定关键字,使能够唯一的确定该类的每个对象实例(该表的每个元组)的属性。

Page 28: 第 9 章  面向对象设计

28

数据接口的实现类似文件系统,也需要提供“对象保存”和“对象恢复”的服务。执行这些服务需要知道被保存或恢复的对象的下述信息: • 它在内存中是哪个对象(以便知道从何处取得被保存的对象,或者把数据恢复到何处); • 它属于哪个类(以便知道该对象应保存在哪个数据库表中); • 它的关键字(以便知道该对象对应数据库表的哪个元组)。 ( 3) OO数据库 将对象和关系作为数据储存。提供了继承和抽象数据类型,不需要对象和存储实体之间的格式转换,不需要另外设计数据接口。需熟悉 OODBMS提供的 ODL、 DML,实现对类和对象的定义以及对数据库的访问。 下图显示了使用关系数据库对类的实例的存储。

Page 29: 第 9 章  面向对象设计

29

服务机构

discount_rate : float = 0.0

price( )

price( )

price交通工具维护

part_numberdiscount_rate

零件

min_quantity : floatcurrent_quantity : floatprice : float

产品

vehicle maintenance ID discount_rate price

1 10 20 2 10 35 3 15 5

part Xvehicle ID part_number

1 1 2 12 3 6

part part_number

min_quantity

current_quantity price discount

1 10 20 134.00 0.0 2 10 14 6.50 0.0 3 15 25 21.75 5.0

0 .. * 0 .. *

用关系数据库实现类

两个多对多关系的类映射为 3 个表(维护表、车辆零件表、零件表)。

Page 30: 第 9 章  面向对象设计

30

7 、确定边界条件 设计中的大部分工作都与系统稳定的状态行为有关。但必须考虑边界条件:系统如何启动、初始化、关闭以及故障处理。 初始化包括:常量、参数、全局变量、任务及保护独享的处置设置。系统关闭时,应该释放所拥有的全部资源。并发系统中必须通知其他任务,系统要关闭。 运行中出现故障的原因可能是: • 用户错误,系统应帮助用户纠正错误。 • 硬件错误,网络连接故障等情况需要保存临时状态。 • 软件故障,在程序中多设计出现故障后的出口。 系统设计是不断迭代和演化的过程,要保证设计模型是正确的、完整的、一致的、现实的、易读的。

Page 31: 第 9 章  面向对象设计

31

8 、评审 如果分析模型与设计模型映射(如:每个子系统都能追溯到一个用例或一个非功能需求),则设计模型是正确的; 如果每个需求和每个系统设计问题都提到了,则模型是完整的; 如果一个模型不包括任何冲突,则它是一致的; 如果模型能够实现,则它是现实的; 由非系统设计人员能够看懂模型,则模型是易读的。

Page 32: 第 9 章  面向对象设计

32

9.3 对象设计 系统设计相当于大楼的建筑平面图,规定了每个房间的用途,以及房间与房间之间、房间与外部环境之间的连接机制。对象设计着重于每个房间的内部细节。 对象设计的主要任务是: • 定义对象完整的接口 • 设计对象内部结构 • 构件选择 • 重组及优化 系统分析确定了问题域对象,以及它们之间的关系、相关的属性、操作。系统设计确定了子系统和大多数重要的求解域对象。对象设计要精细这些对象(这里的对象包括子系统),并可能定义其他的求解域对象。

Page 33: 第 9 章  面向对象设计

33

1 、定义对象的接口 对象的接口也称为对象的协议、对象的界面。它通过定义对象可以接收的每个消息和当对象接收到该消息后完成的相关服务来描述。接口提供了一种方法,把对象基于操作的功能说明与具体实现区分开来,使得任何依赖和使用接口的客户不必依赖于接口的具体实现,有利于接口实现的替换。 接口描述可以用 UML 中类图一样的符号,省略属性部分,《 interface》要包含在类名部分中。比较多的人喜欢用程序设计语言来定义接口,以便用编译器来发现接口描述中的错误和不一致。 下图给出了“转账” 的 Java 接口描述。

Page 34: 第 9 章  面向对象设计

34

//provided interfaces:Public interface Transfers{ public Account create (Customer owner, Money

balance, AccountNumber account_id); public void Deposit (Money amount ,String reason); public void Withdraw (Money amount, String reason);} 要确定某个对象完整的接口,必须考察与它有关的所有用例,将与它有关的所有消息抽取出来,形成该对象完整的界面。 对于包或构件,当有依赖关系指向它的时候,就有可能表示该包或构件需要提供一个接口。

Page 35: 第 9 章  面向对象设计

35

2 、设计对象内部结构 • 确定遗漏的属性和操作: 系统分析和设计时集中考虑应用域,忽略与实现相关的细节,这时就应该增加上。 • 指定类型,声明可见性: 属性:确定类型、数据结构。除了分析活动中确定的属性,还包括一些其他属性,这些属性用来表示和其他类的对象关联的对象引用(关联的实现)。 操作:确定参数、返回值及类型。 为了确定每个属性和操作的访问权限, UML定义了

3种可见性符号,即在属性和操作的说明前加上前缀:

Page 36: 第 9 章  面向对象设计

36

- :私有的, 只能由定义它的类访问,子类和其他类 都不能访问。 + :公有的, 任何类都可以访问。公有的操作确定了 对象的接口。 # :保护的, 可以由定义它的类以及该类的子类访问。 • 设计关联: OOPL “一般不提供 关联”的直接实现,一般用指针或对象引用来实现关联。(有 UML建模工具自动完成关联到引用的转换)。 单向一对一关联:

ZoomIn MapArea ZoomIn1 1 MapArea-Target: MapArea

Page 37: 第 9 章  面向对象设计

37

对于双向一对一关联的实现,则在 MapArea 中设置一个引用 ZoomIn 的属性。并增加相应的操作,修改属性并避免无穷循环。 一对多关联:

Layer Element1 *

Layer Element-LayerEle: Set -Containe: Layer

Layer的 Set取决关系的约束条件。如层中的元素要排序,则用 Array或 Vector 代替 Set 。双向一对多关联的实现

Page 38: 第 9 章  面向对象设计

38

多对多关联:Record Element* *

Record Element-RecordEle: Set -Containe: Set

双向多对多关联的实现 对于多对多关联,可以将 “关联” 作为一个独立的类,形成两个二元关系,可降低重数,方便实现。

Page 39: 第 9 章  面向对象设计

39

• 设计操作的算法 分析类的状态图,从每个状态转移前后的动作说明获取每个方法体的逻辑结构。而顺序图中的消息一般对应状态图中引起状态转移的事件或动作。

类名可见性:属性列表可见性:操作 1(参数表 )

可见性:操作 2(参数表 )

……

方法 1

方法 1 过程体 方法 2

方法 2 过程体

方法 n

方法 n 过程体

…消息

消息

消息

Page 40: 第 9 章  面向对象设计

40

3 、构件选择 选择系统运行的软、硬件平台,包括商品构件(更可靠、有效、健壮)、 DBMS 、中间件、企业应用程序框架(特定的应用)等,目的是尽可能多地减少需要开发的自定义对象的数量。由于商品构件支持大多数系统,较为复杂,需要学习的投入,可能还要作适应性修改。4 、重组与优化 ( 1 )提高可复用性 对象设计给出了开发阶段中再次检查应用程序和求解对象间继承层次的机会。 设计完善的继承层次的主要优点是: • 可以复用更多的代码,产生较少的冗余。 • 代码是可扩展的,可用来创建更特别的类。

Page 41: 第 9 章  面向对象设计

41

但是通过继承复用是有代价的,开发人员需要正确的预见所创建的类的哪些行为需要共享、哪些行为需要由以后的新类细化,通常还不会知道以后所有可能的新类。另外,一旦开发人员为共享代码定义了继承层次,抽象类的接口难以改变,因为许多子类和客户类都依赖它们。 设计通过继承层次复用的方法是: • 检查大量相似的类,抽取出它们的共同行为。 • 给出一层抽象概念,并从预期的变化中抽取出一个具体类。如 AbstractFactory等设计模式(见下一节

“ ”设计模式 )都使用了继承来防止预期的变化。

Page 42: 第 9 章  面向对象设计

42

( 2 )优化访问路径 效率低的系统性能的常见原因是访问必须的信息时对多个关系的重复遍历。为了识别较低的访问路径, Rumbaugh 建议对象设计者应该考虑下列问题: • 对于每个操作:需要多少次遍历?遍历哪些关系?常用的操作不应该有许多遍历,应该直接通信。如果缺少直接通信,应该在两个对象间增加另外的关系。有一个

Demeter法则,称“只同你的直接朋友对话”,指在软件设计中,一个方法只与由关联连接的相邻对象通信。好处:易理解、易修改、效率高。 • 对于每个关系 “ ”:有 多重 关系?重数是必需的吗?检

“ ”索过程中,关系中的 多 端是否经常出现?如果有,应该“ ” “试着将 多 减少为 1” “ ”。否则,应为 多 端排序或建立索引以改进访问时间。

Page 43: 第 9 章  面向对象设计

43

效率低的另一个原因是过多的建模。分析时确定了许多类结构,但设计时发现没有任何意义的信息。因此对象设计者应该问: • 对于每个属性:哪些操作用到了这个属性?只有 set()、 get()操作吗?如果是,能否移到调用它的对象中去?如果某些类有很少的属性和行为,并且与其它类相关,可将这些类退化成属性(减少了类的数目)。这样做的目的是使模型变得简单、直接。

Page 44: 第 9 章  面向对象设计

44

9.4 设计模式 1 、概述 模式 (Pattern) 是解决特定领域问题的经验,可以帮助人们在软件开发过程中对于经常重复出现的问题制定成功解决的方案。 模式的概念最初来自于建筑学领域,用模式描述建筑物的建筑元素( Alexander, 1979 ),它合并了被认为是好的设计的实践经验。 90年代中期,软件设计人员认识到了这些重复出现的软件设计问题。 94年 Gamma等 4人(简称“ Gang of Four”)合著的《设计模式:可复用面向对象软件的基础》提出了用设计模式进行解决,并对设计模式进行了分类描述和解释。 96年由 Buschmann等5 人合著的《面向模式的软件体系结构》将模式跨越不同的抽象层次,提出了高层的体系结构模式、中层的设计模式和低层的习惯用法。本章主要针对设计模式进行讨论。

Page 45: 第 9 章  面向对象设计

45

Gamma 提出:设计模式解决特定的设计问题,并使得面向对象设计更灵活、优美和可复用。它们通过将新的设计基于以前的经验之上而帮助设计者复用成功的设计。熟悉这样的模式的设计者可以立即应用它们到设计问题中,而不需要重新去发现它们。 因此,在 OOD 过程中,开发人员应积极去选择并应用现存的可复用的设计模式,而不是试图创建新的设计模式。 每个模式都有伴随定义的语境和强度。语境解释了模式适用的情况。强度是语境中的元素,有某种程度上的不同。如果问题的环境与模式的语境和强度相匹配,该模式就适合于你的应用。如果模式限制必须有灵活的环境,使用模式设计就要付出代价。

Page 46: 第 9 章  面向对象设计

46

由于设计模式的复杂性和抽象性,软件设计人员一般从以下几方面考虑选择适合的设计模式: • 考虑设计模式解决设计问题的步骤,从中借鉴良好的设计经验。 • 考虑设计模式所要解决的问题,将之与自己的问题匹配,从而做出选择。 • 从更高一层着眼,分析所有的设计模式之间的关系,研究目的相似的模式(要求设计人员对设计模式非常的熟悉)。 • 考虑设计中哪些是可变性和可扩充性。 设计模式在软件设计中的应用主要取决于设计人员的主观意识和熟悉模式的程度。

Page 47: 第 9 章  面向对象设计

47

2 、设计模式的分类 根据“ Gang of Four” 的分类准则,按模式的使用目的(即“用来完成什么工作”)来划分,可分为以下几种类型: 创建型:创建对象 结构型:处理类和对象的组合 行为型:对类或对象如何交互、如何分配职责进行描述 ( 1 )创建型模式 • Abstract Factory(抽象工厂):提供了创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。 • Builder(生成器):将一个复杂对象的构建与它的表示分开,使得同样的构建过程可以得出不同的表示。 • Factory Method(工厂方法):定义了一个创建对象的接口,让子类决定将哪个类实例化。该方法使一个类的实例化延迟到其子类。

Page 48: 第 9 章  面向对象设计

48

• Prototype(原型):用原型实例指定创建对象的种类,并通过复制原型来创建新的对象。 • Singleton(单件):一个类仅有一个实例,提供对它全局访问。 ( 2)结构型模式 • Adapter(适配器):将一个类的接口转换成客户希望的另一个接口。该模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 • Bridge(桥接):将抽象部分与它的实现部分分离,使它们都可以独立的变化。 • Composite( “组合):将对象组织成 整体 - ”部分 的层次结构。该模式使得客户机对单个对象和复合对象的使用具有一致性(进行同样的交互)。 • Decorator(装饰):动态的给一个对象添加一些额外的功能。就扩展功能而言,该模式比生成子类方式更为灵活。

Page 49: 第 9 章  面向对象设计

49

• Facade(外观) :为子系统中的一组接口提供了一个统一的界面。该模式有助于为复杂子系统提供一个简单接口,使得子系统容易使用。 • Flyweight(享元):运用共享技术有效的支持大量细粒度的对象。 • Proxy(代理):使一个对象(如组件的客户机)与一个对象代表而不是对象本身通信。 ( 3)行为型模式 • Chain of Responsibility(职责链):为避免请求的发送者与其接受者耦合在一起,给予多个对象都有处理这个请求的机会。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。 • Command(命令):将请求分装为对象(将请求与其执行分开),允许系统以不同请求、队列或日志请求作为参数来表示客户,并支持无法执行的操作。

Page 50: 第 9 章  面向对象设计

50

• Interpreter(解释器):给定一种语言(如脚本语言),定义语法的表示方法和解释器,解释器使用该方法来解释语言。 • Iterator(迭代器):提供了连续访问一个聚集对象中各个元素的方法,而不需要暴露该对象的内部表示。 • Mediator(中介者):定义一个中介对象,来封装一组对象交互的方式。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且还允许对象的交互独立变化。 • Memento(备忘录):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,使其以后能够恢复。 • Observer(观察者):在对象间定义的一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

Page 51: 第 9 章  面向对象设计

51

• State(状态):允许一个对象在其内部状态改变时改变它的行为。对象会改变类。 • Strategy(策略):定义一系列的算法,对每个算法进行封装,并使它们能够交互。该模式使得算法的变化可独立于使用它的客户。 • Template Method(模版方法):定义操作中算法的框架,将某些步骤推迟到子类中进行。该模式允许子类在不改变算法结构的情况下,改进算法的某些步骤。 • Visitor(访问者):表示作用于某个对象结构中的各元素的操作,使得在不改变各元素的类的前提下定义作用于这些元素的新的操作。 “以上是 四人帮”模式,还有其他模式。“五人帮”对模式的扩展及增加的分类准则同学们可看相关书籍。

Page 52: 第 9 章  面向对象设计

52

例 1 :使用设计模式消除实现的依赖性 如编写一个能够在多种风格的窗口上运行的程序。程序本身不用知道或依赖于窗口、滚动条、按钮、菜单

等对象特定的、不同的外观感觉。 Abstract Factory模式为每个可替代的对象(如抽象窗口、抽象按钮)提供一个抽象类及其接口,由每个具体类(称为 factory)实现它的抽象类的接口操作。(见下图) 注意到,MotifyFactory和MacFactory有着同样的接口 createButton(),但产生的按钮不一样,客户只需访问抽象类中的接口。该模式支持将接口与具体实现分开,这使得将来如果增加新的 factory,不会改变应用程序。

Page 53: 第 9 章  面向对象设计

53

AbstractFactory

createWindow() createButton()

MotifFactory

createWindow() createButton()

MacFactory

createWindow() createButton()

抽象窗口 抽象按钮

Motif 窗口 Mac 窗口 Motif 按钮 Mac 按钮

客户

AbstractFactory 设计模式(虚线箭头表示调用关系)

提供接口实现接口( 创建按钮 )

只需访问接口和抽象类

Page 54: 第 9 章  面向对象设计

54

命令execute()

具体命令 2execute()

具体命令 1execute()

接收者action1() action2()

用户 选择

捆绑

例 2 :使用 Command 模式对控制流进行封装。 在交互式系统中,希望在不知道请求内容的情况下,能实现执行、取消执行或存储用户请求。把请求与处理分开的关键在于把请求变成命令对象,它继承了抽象命令类。命令类定义了如何执行、取消执行或存储指令,而具体的类实现特定的请求。 Command 模式允许封装控制,以便与特定请求相独立,平等对待用户请求。

Command 模式

Page 55: 第 9 章  面向对象设计

55

命令execute()

粘贴命令execute()

拷贝命令execute()

文件paste() copy()

菜单项

捆绑

* 菜单 *

如:可用 Command 模式把菜单选项与活动分离开来(如下图),把控制流集中到命令对象中,不同的命令对象实现不同的执行请求(拷贝命令、执行命令),而不是分布到边界对象(菜单项)和实体对象(文件)中去。

Command 模式的例子

Page 56: 第 9 章  面向对象设计

56

例 3 :使用 Proxy 模式的设计 Proxy 模式有许多用途:提高效率、易于存取、防止越权访问等。一个客户机需要访问另一个组件的服务,但对组件进行直接和无限制的访问可能是低效的甚至是不安全的,需要额外的控制机制。 Proxy 模式使客户机与组件代表而不是组件本身通信。这种代表(称为代理)提供组件接口并执行附加的前期处理和后期处理。 Proxy 模式的模版为: 抽象原件

服务 1()服务 2() 原件

服务 1()服务 2()

代理服务 1()服务 2()

客户机 任务

Page 57: 第 9 章  面向对象设计

57

其中: 客户机的责任: • 利用代理提供的接口来请求特殊服务。 • 完成它自己的任务。 抽象原件的责任: • 对代理和原件,服务作为一个抽象基类。 原件的责任: • 实现一个特殊服务。 代理的责任: • 对客户机提供原件接口。 • 确保安全、有效和正确的访问原件。 下面给出了 2 个具体例子说明使用 Proxy 模式提高运行效率的设计和防止越权访问的设计。

Page 58: 第 9 章  面向对象设计

58

( 1 )考虑将表示图片的对象存入文件。从文件中装入所有组成图片像素的代价是昂贵的,显示图片之前没有必要装入所有图片数据。用 Proxy 模式可以实现这种优化。 Image

filename:String data: byte[ ]width( ) height( ) paint( )

Image

filename:Stringwidth( ) height( ) paint( )

RealImage

data: byte[ ]width( ) height( ) paint( )

ImageProxy

filename:Stringwidth( ) height( ) paint( )

转换前的设计

转换后的设计

图像 1 0..1

Page 59: 第 9 章  面向对象设计

59

ImageProxy 提供了和 Image 一样的接口,某些操作(如 width()和 height() )由 ImageProxy处理,但是当需要画出 Image 的时候, ImageProxy才从磁盘中读入数据并生成一个 RealImage 对象。如果客户不调用 paint() 操作,就不用创建 RealImage 对象,节省了实际的计算时间。调用的类只通过 Image 接口访问 ImageProxy和RealImage 。

( 2 )银行信息系统的一个例子规定:经纪人不能访问由其他经纪人管理的一些档案。因此必须对系统的访问权限动态建模。下图给出了用 Proxy 模式实现的访问。 对每一个档案,创建了档案代理以保护档案并检查访问权限。合法经纪人与档案代理之间的访问关系指出了经纪人可以访问哪个文件。为了访问档案,经纪人先给档案代理发送消息,档案代理先检查发出调用的经纪人是否与档案代理有访问关系。如果授权访问,档案代理将认证操作发给实际的档案对象。

Page 60: 第 9 章  面向对象设计

60

图中访问关系类包含有一组经纪人可以访问档案的操作。档案代理中的每个操作首先调用 isAccessible() 操作,检查发出调用的经纪人是否具有合法的访问权。一个访问关系可以用于多个授权的访问控制。

档案代理buy( )

sell( )

estimateYield()

档案buy( )

sell( )

estimateYield()

1 1

访问isAccessible()

1经纪人 *

Page 61: 第 9 章  面向对象设计

61

3 .模式的基本元素和描述模版 一般而言,一个模式有四个基本要素: ( 1 )模式名称( pattern name ): 对某一模式的简练概括所提取的名字。 ( 2 )问题 (problem) : 描述了设计模式所解决的问题,或者说使用设计模式可以在设计中避免的一些缺点。 ( 3 )解决方案 (solution) : 给出了问题的解决方案,描述了该方案设计中的组成成分以及它们之间的相互关系、职责和协作方式。 ( 4 )效果 (consequences) :对应用某种设计模式的一种权衡,侧重于对时间和空间的衡量,分析了应用设计模式之后的优点和缺点。

Page 62: 第 9 章  面向对象设计

62

设计模式有以下几种描述方法: ( 1 )自然语言描述法 采用自然语言来描述设计模式及其所解决的问题。自然语言比较容易理解,对于设计模式的理解比较方便。但是,自然语言过于随便,没有客观性,难于提供一个从现实中所要解决问题到设计模式之间的良好过程。( 2) UML描述法

UML描述方法是 Gamma 等人在介绍了设计模式时采用的方法,该描述法清晰和统一,符合大部分软件设计人员的习惯。采用 UML 中的类图不仅可以描述设计模式中的组成部分,而且可以方便的描述模式中类及对象之间的关联、聚合、继承和各种依赖关系。

Page 63: 第 9 章  面向对象设计

63

( 3 )形式化方法 形式化方法主要利用形式化规格说明语言对软件设计模式进行严格地描述,并采用数学推理的方法应用设计模式来改进软件设计质量。主要用于科学研究及一些要求比较高的软件设计。 对于软件设计而言,模式的语义部分大部分采用自然语言来描述,而解决方案采用 UML描述。 描述模版: • 模式名与分类 • 意图(做什么、基本原理) • 适用性 • 模式结构 • 参与者 • 效果

Page 64: 第 9 章  面向对象设计

64

4 、设计模式引入软件设计中的一般步骤 对现有的软件设计模式的应用一般有两个方面: • 在软件系统设计开始阶段,就应用设计模式对软件体系结构(往往是子系统结构)进行设计。也就是从软件设计一开始就从应用设计模式。 • 在系统初步设计完成后,针对系统内某些组件或模块的性能要求,在设计方案中加入某个设计模式使系统的某些模块更加优化和灵活,也就是在系统整体设计后用设计模式对系统的部分结构进行优化。 由于设计模式的复杂性,使得模式在提出很长的一段时间后,设计人员在将其应用到具体的软件系统设计中时,仍然存在很多困难。主要原因有两点:

Page 65: 第 9 章  面向对象设计

65

• 对软件设计模式的总体把握以及具体模式的理解都还不够透彻。 • 没有一个有效的方法和步骤来指导软件设计人员如何应用这些设计模式。 软件设计模式应用于软件设计中的一般步骤: ( 1 )划分求解域的类型 软件设计模式的三个类型(创建型、结构型和行为型),用于分别解决不同类型的问题。因此要对所要解决的问题进行抽象,判断问题属于创建型,还是结构型,或者行为型。所要解决的问题可能有多种类型组成,也可能不涉及到现有的软件设计模式。

Page 66: 第 9 章  面向对象设计

66

( 2 )判断是否可以借鉴某种类型的设计模式来设计或优化。如果符合某种类型范畴,则从该类型的模式中选择合适的模式。 该阶段要求设计人员: ·理解所选择的模式,注意模式的适用条件和模式的使用效果,确定是否适合要解决的实际问题。 ·研究模式的结构、组成、类及对象的协作等关系。 ( 3 )规划问题和匹配模式。 将要求解的问题与选择的设计模式所能解决的问题进行比较,找出共性。在求解的问题域内考虑那些元素对应现有模式中的类,以及模式中各角色的如何确定等等。如果发现选择的设计模式并不合适,返回到上一步重新进行设计。

Page 67: 第 9 章  面向对象设计

67

( 4 )对选取的模式进行变体,以适应问题的需要。 在应用某个设计模式时,往往会发现并不完全适合所要解决的问题。同时,由于现有的模式的一些局限性需要对其进行扩充。因此要对该模式的原始结构进行修改,使之适应系统的需要。 ( 5 )对使用模式后的软件体系结构进行精化。

Page 68: 第 9 章  面向对象设计

68

5 、例: 设计模式在行业安全管理平台设计中的应用研究 本系统目标是为电力行业提供一个绘制电力接线图的平台,使之产生其它应用系统的输入。该平台有两个要求:首先,输出必须是矢量图。这样,图形不会产生放大缩小失真问题。其次,图形必须具有智能连接关系的判断。换句话说,图形必须能够判断所处的图形位置,并根据位置信息和电气元件状态来判断是否带电,从而呈现不同的颜色。正因为如此,要求产生的接线图包括联结关系属性,并且易于使用。 ( 1 ) 系统分析 对问题进行分析,分析该平台中对象的粒度关系。如下图所示:

Page 69: 第 9 章  面向对象设计

69

对于电力系统接线图而言,由于包含电路逻辑连接信息,所以,整个连接图由逻辑单元组成。逻辑单元由逻辑上的电气元件组成。电气元件在系统中的表示由点、线、圆等基本几何元素构造而成。 除了粒度之外,该系统要为用户提供的一个矢量图形编辑环境,能够快速绘制电力系统接线图,必须轻便灵活、简单方便,具有强大的电力图元集。电力图元是电力接线图中逻辑上最小的单元,例如,开关,变压器等等。 然而,由于各地需求的差异,还必须保证该系统具有良好的扩充性和适应性,不用因为图元或元件的标识差异而重新开发或维护。

Page 70: 第 9 章  面向对象设计

70

为此,将系统分为两个大模块: • 基于元件的连接图形生成模块 • 为用户提供一个可以通过对基本元素组合定制元件的图形符号及属性(图元)的模块。 只有这样,系统才可以适应各地电力单位不同的需要而不必重新开发程序。这里充分体现了系统对扩充性要求。 因此,系统在采用面向对象设计技术设计时,拟采用已经发布的优秀设计模式,实现复用性和灵活性。由于要创建图形对象,首先想到的就是选择创建型设计模式。然而,创建型模式能否很好的满足我们的要求以及如何选择合适的创建型模式,需采用下面所描述的过程来进行分析。

Page 71: 第 9 章  面向对象设计

71

( 2 ) 应用设计模式进行初步设计 ① 分析问题,划分问题类型。 如上所述,该系统主要产生矢量电力接线图。通过对该领域的分析和抽象,可以判断出问题应该属于创建类型。 但是,创建型设计模式中包含了抽象工厂( Abstract

Factory )、生成器( Builder )、工厂方法( Factory Method )、原型( Prototype )、单件( Singleton )等几种对象创建型模式,为了选择适合的设计模式,进而做下一步分析。

② 选择适合的创建模式 在所有创建型设计模式中, Singleton 模式主要应用于系统中要求某个类在该系统中只有一个实例的情况。因此,可以排除 Singleton 模式。

Page 72: 第 9 章  面向对象设计

72

Builder 模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,并不满足系统对图元可扩充性的要求。 Factory Method 模式,主要产生一个用于创建对象的接口,让子类决定实例化哪一个类,使得一个类的实例化延迟到其子类。对于所要设计的平台而言,增加任何一个新的图元,必须增加相应的工厂类的实例,随着系统内子类数目的激增,会导致系统性能下降,后期程序的维护过于复杂。显然,也不符合系统对图元扩充性的要求。 Abstract Factory并没有很大的改进,因为它需要一个同样庞大的设备类别层次。

Page 73: 第 9 章  面向对象设计

73

Prototype 模式对电力逻辑平台框架可能是最好的,它仅需要为每个图元类实现一个 Clone (复制)操作,这样可以减少类的数目。由于电力接线图是多个图元的多个实例组成,显然 Prototype 可以较好的满足这一点。 该模式的结构示意图见下图 1 。 ③ 规划和匹配所选择的设计模式 匹配 Prototype 模式得到的设计结构参见下图 2 。

Page 74: 第 9 章  面向对象设计

74

其中 Prototype :定义一个复制自身的接口 ConcretePrototype ;实现一个复制自身的操作 Client :让一个原型复制自身从而创建一个新的对象

图 1 Prototype 模式结构

+Operation()

Client

+Clone()

Prototype

+Clone()

ConcreterPrototype1

+Clone()

ConcreterPrototype1p = Prototype->Clone;

return copy of self; return copy of self;

Prototype

Page 75: 第 9 章  面向对象设计

75

+Manipulate1()+Manipulate2()

GraphTool

+Create()+Connect()

GraphCreateTool

+Draw()+Clone()

TDevice1

+Draw()+Clone()

TDevice2

+Draw()+Clone()

TDevice3

+Draw()+Clone()

TDevice

1

*

+Rotate1()+Rotate2()

RotateTool

+ZoomIn()+ZoomOut()

ZoomTool

p = TDevice->Clone();while ( MouseDown ){ p->Draw();}insert p into one graph;

图 2 电力行业绘图平台体系结构图

Page 76: 第 9 章  面向对象设计

76

④ 应用 Prototype 模式达到的效果 采用 Prototype 模式,通过用 TDevice 原型实例指定创建图元的种类,并且通过其自身的拷贝来创建新的对象。应用该模式使得系统扩充性较好,增加新的设备类别时可以不用改动程序。 ⑤ 系统性能的考虑。 对所要解决的问题进行抽象并与所选择的模式进行匹配,得到上面的设计结构。采用对象来表示图中的每个设备,极大地提高应用程序的灵活性,但是却占用大量的存储空间,往往会影响系统的空间性能。因此,考虑应用其它设计模式对上述结构进行进改,提高其性能。

Page 77: 第 9 章  面向对象设计

77

( 3 ) 应用 Flyweight 模式完善设计 为了解决性能问题,可以抽象出设备的描述信息,将这些描述信息与电力系统接线图中的设备元件显示进行分离。这样,在图中不必对每个元件(图元)都实例化其所属的设备类。分析如下: ① 判断问题类型 由于系统所要的客观对象在系统中已经有了相应的映射。此时,应该通过对系统中类和对象进行不同的组合和分解以获得更大的灵活性和更好的系统性能。所以,应该选择结构型设计模式对原有系统结构进行改进。。

Page 78: 第 9 章  面向对象设计

78

② 模式的选择 Flyweight 模式能解决由于系统中存在大量类似的、具有共性的对象的而严重影响系统的性能的问题。 可将对象的这些共同信息提取为一个新的对象

Flyweight ,这样,原来许多对象都需要的、重复的信息描述只需要在一个共享的 Flyweight 对象中描述就可以了,可以节省存储空间。 所以,引入 Flyweight 模式对电力逻辑平台体系结构进行了修改。

Flyweight 模式的结构图如下图:

Page 79: 第 9 章  面向对象设计

79

+OperationByextrincState()-InstrinsicState

ConcreteFlyweight

+OperationByextrincState()

Flyweight

+OperationByextrincState()-AllStateUnsharedConcreteFlyweight

if ( Flyweight[key] exists ) return existing flyweight;else{ create new flyweight; Add it to pool of flyweights; return new flyweight;}

Client

+GetFlywightByKey()

FlyweightFactory1 *

Flyweight 模式结构图

Page 80: 第 9 章  面向对象设计

80

其中: • Flyweight :描述了一个接口,通过这个接口, Flyweight 可以接受并作用于外部信息(对象的位置、 连接、大小等状态)完成相应的操作。 • ConcreteFlyweight :实现 Flyweight 接口并为内部状态 (自身信息)增加存储空间。该类对象必须 是可共享的,它所存储的状态必须是内部的。 • UnsharedConcreteFlyweight :可以不共享的类。 • FlyweightFactory :创建并管理 Flyweight 对象,确保合理 的共享 Flyweight 。 • Client :维持一个对 Flyweight 的引用,计算或存储一个或 多个 Flyweight 的外部状态。

Page 81: 第 9 章  面向对象设计

81

③ 模式与实际问题的匹配 Flyweight 对象为所有接线图中的该类图元共享,即接线图中的该设备的实例共享该设备的 Flyweight 对象中的描述信息。一般将该设备描述信息(设备图示符号)称为设备的内部信息。这样,通过对大量对象共性的抽象和提取,会大大减少系统所占用的存储空间。 但是,除了设备的内部信息外,系统接线图中的元件图示必须拥有足够的外部信息来描述:比如连接关系、位置信息、图形大小等信息。所以,必须为外部信息生成一个新的抽象类进行描述,设计中将该类定义为

TNode ,用以描述元件在系统接线图中连接、位置等信息。 具体改进后的的设计结构参见下图,其中增加的部分用虚线框标出。

Page 82: 第 9 章  面向对象设计

82

+Manipulate1()+Manipulate2()

GraphTool

+Create()+Connect()+GetDevice()

GraphTool

+Draw()+GetDevice()

TDevice1

+Draw()+GetDevice()

TDevice2

+Draw()+GetDevice()

TDevice3

+Draw()+GetDevice()

TDevice

1

0..*

+Rotate1()+Rotate2()

RotateTool

+ZoomIn()+ZoomOut()

ZoomTool

p = TNode->Clone();p->pDevice = GetDevice();while ( MouseDown ){ p->pDevice->Draw();}insert p into one graph;

+Clone()

TNode-TDevice* pDevice

? ?

1

1..*

电力行业绘图平台改进后的结构示意图

Page 83: 第 9 章  面向对象设计

83

系统图中 Tnode 是引入享元模式后加入的类,是为了计算或存储一个或多个 TDevice 的外部状态。连接图中的每一个设备图示都对应一个 TNode ,但同一个设备类别的信息描述只有一个(在 TDevice 中),这样才会节省存储空间。 TDevice 中保留了图形类别描述的内部信息,只有得到外部信息才可以在电路连接图中绘制出设备图示。 模式与系统中的元素对应关系总结如下 : TdeviceFlyweight Tdevice1-3ConcreteFlyweight TnodeClient 没有不共享的对象 ,即设计中不存在 UnsharedConcreteFlyweight FlyweightFactory GraphTool

Page 84: 第 9 章  面向对象设计

84

在实现时,每个 TNode 中放一个指向具体 TDevice的指针,共享 TDevice 中的类别描述。比如: TNode1,TNode2 都是开关,但它们都没有具体类别描述信息,可都有一个指针指向名为 TDeviceKaiguan 的对象,共享这个设备对象的类别描述 .

需要说明的是,在设计中与标准的 Flyweight 模式有所不同。 Flyweight 共享对象,也就是 TDevice ,是在系统初始化时候由系统来创建。 ④ 使用模式后的效果 随着安全管理平台在各地电力企业的实施,会不断的增加设备。这就使得共享的设备 Flyweight增多,节约的存储空间随着共享对象的增多而增大。由于不同的设备对象数远远小于接线图中的所有电力图元实例数,因此,大大的节约了存储空间资源。

Page 85: 第 9 章  面向对象设计

85

( 4 ) 应用 Builder 模式提高软件设计的灵活性 通过应用两种设计模式,使得绘图平台的稳定性和适用性大大提高。稳定性体现在可以任意增加新的设备类别,而无需修改程序。适应性表现在由于设备类别的可扩充性,系统可以适用各地电力企业的需要。 但是随着平台的使用,出现了新问题。主要是企业机械设备类别的增加使系统的设备类别剧增,增加了系统的运行的负担。所以必须改进 Flyweight 模式中共享对象(设备类别描述)的构建方式。 改进对象的构建方式,必然从创建型模式中寻找合适的模式来解决问题。电力设备的图示可以通过基本几何图形(线段、圆形、矩形)的不同组合来搭建不同的设备图示。而 Builder 模式可将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。所以采用 Builder 模式来改善对象创建过程。下面给出 Builder 模式结构图和应用 Builder 模式改进后的系统设计结构示意图。

Page 86: 第 9 章  面向对象设计

86

+Construct()

Director

+BuilderPart()

Builder

Product

for all objects in the structure{ Builder->BuildPart();} +BuilderPart()

+GetResult()

ConcreteBuilder

1 *

Create An Instance

其中:( 1) Builder :为创建一个 Product 对象的各个部件指 定对象接口。( 2) ConcreteBuilder :实现 Builder 的接口以构造和装配该产 品的各个部件。( 3) Director :构造一个使用 Builder 接口的对象。( 4) Product :表示被构造的复杂对象。

Builder 模式结构图

Page 87: 第 9 章  面向对象设计

87

+Manipulate1()+Manipulate2()

GraphTool

+Create()+Connect()+GetDevice()

GraphTool

+Draw()+GetDevice()

TDevice1

+Draw()+GetDevice()

TDevice2

+Draw()+GetDevice()

TDevice3

+Draw()+GetDevice()

TDevice

1

0..*

+Rotate1()+Rotate2()

RotateTool

+ZoomIn()+ZoomOut()

ZoomTool

p = TNode->Clone();p->pDevice = GetDevice();while ( MouseDown ){ p->pDevice->Draw();}insert p into one graph;

+Clone()

TNode-TDevice* pDevice

? ?

1

1..*

+DrawPicMeta()

TPicPart-InsideState-nType

+DrawPicMeta()

TCircle-InsideState-nType

+DrawPicMeta()

TRectangle-InsideState-nType

+DrawPicMeta()

TLine-InsideState-nType

1

*

for all objects in PicPatrList { GetInsideState(); switch ( nType ){ case Line: DrawPicMeta(TLine); case Rectangle: DrawPicMeta(TRectangle); case Circle: DrawPicMeta(TCircle); }}

1

*

1

*

应用 Builder 模式改进后的系统设计结构示意图

Page 88: 第 9 章  面向对象设计

88

从图中可以看出,将 TDevice匹配为 Builder 模式中使用接口的 Director , 定义一个新的抽象类 TPicPart(子类分别是 TLine、 TRectangle、 TCircle ),提供给 TNode 一个构造产品的抽象接口。该接口使得生成器隐藏这个设备图示的表示和内部结构以及该设备图示是如何由基本几何元素装配的过程。

只需在 TDevice 中存储几何元素的简单信息,通过TPicPart 抽象接口,可以方便的绘制各种电力设备图示( Product ),当完成时才从生成器中取回它,大大提高了设计的灵活性。

Page 89: 第 9 章  面向对象设计

89

9.5 RUP 设计活动 RUP 的设计活动主要产生设计模型和实施模型。模型的创建是由构架设计启动的。 软件系统的构架是对以下问题决策的总和: • 软件系统的组织; • 对组成系统的结构元素、接口以及这些元素在协作中的行为的选择; • 由这些结构元素与行为元素组合成更大子系统的方式; • 用来指导将这些元素、接口、它们之间的协作以及组合起来的构架风格。 软件构架提供了整个系统的清晰的视角,它不仅涉及到静态结构与动态行为,而且涉及使用、功能、性能、适应性、重用和可理解性等。它能够指导系统的开发工作,可以有效地理解、组织开发并改进这个系统。

Page 90: 第 9 章  面向对象设计

90

1 、构架设计 设计系统的总体结构,主要由构架设计师完成。设计目的是通过对如下内容的识别来勾画设计与实施模型及其构架: • 实施模型的结点及其网络配置。 • 设计模型中的主要子系统及其接口。 • 有重要意义的设计类。 • 处理具有共性需求(如对象的持久性、分布特征、性能等)的通用设计机制。 设计时要考虑系统的可复用性和可维护性,尽可能复用已有的构件并考虑新设计构件的复用性。为了便于维护,设计简单的类、简单的接口、简单的协议、简单的描述。 下图说明了构架设计的输入和结果。

Page 91: 第 9 章  面向对象设计

91构架设计的输入和结果

需求补充

构架描述构架描述

概要设计类

用例模型

分析模型 概要实施模型

概要子系统概要接口

构架设计

构架设计师

(分析模型视图) (设计模型和实施模型视图)

Page 92: 第 9 章  面向对象设计

92

构架设计的活动: • 创建设计模型的构架视图 设计模型的构架视图展示了设计模型中对构架最为重要的元素,如最重要的子系统和接口,还有一些很重要的类。因此,有以下具体的活动: ( 1 )定义并设计子系统 主要任务是: ① 划分各个子系统 子系统提供了将设计模型组织成可管理片的方法。尽可能的利用分析包去识别子系统并按软件层次来划分。软件层次一般分为:专用应用层、通用应用层、中间件层和系统软件层。如下图所示:

Page 93: 第 9 章  面向对象设计

93

用户管理

操作权限分配 帐户管理 工作流管理

消息管理 事务管理

操作系统 数据库

专用应用层

通用应用层

中间件层

系统软件层

Page 94: 第 9 章  面向对象设计

94

专用应用层是项目中特殊的应用部分,被复用的可能性很小。通用应用层由一些公共构件组成,可从构件库中获取或设计可复用构件。这两层应用子系统尽可能利用分析包去识别。如果分析包不能直接实施到某个结点,则需要将子系统作进一步分解,使得每个子系统可分配到单个结点上。同时,对这些子系统进行精化,使网络流量最小。 中间件和系统软件是一个系统的基础,因为所有功能都是基于下列各种软件之上的: OS、 DBMS 、通信软件、对象分布技术、 GUI 设计工具、事务管理技术等。选择并集成所获取或构造的软件产品是设计阶段关注的要点。 什么是中间件?

Page 95: 第 9 章  面向对象设计

95

中间件是一种独立的系统软件或服务程序,分布式应用软件借助它在不同的技术之间共享资源。中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。中间件分为以下几类: • 消息中间件:控制和管理一个集成的系统,使得组成 这个系统的多个应用之间通过传递消息 完成整个业务流程。 • 事务处理中间件:协调事务在多台应用服务器上实时 并发地运行。 • 数据存取和管理中间件:为在网络上虚拟缓冲存取、 格式转换、解压等带来方便。 • Web服务中间件:对浏览器图形用户界面的功能进行 扩充。 • 安全中间件:提供解决安全问题的特殊功能。

Page 96: 第 9 章  面向对象设计

96

• 跨平台和构架的中间件:分布式系统中,需要集成 各结点上的不同系统平台上的构件或新老版本 的构件: CORBA、 JavaBeans、 DCOM。 • 专用平台中间件:为特定领域(如电子商务、网站 等)建立相应构架、配置相应的构件库和中间 件,开发和运行关键任务。 • 网络中间件:包括网关、接入、网络测试、虚拟缓 冲等。 在实际应用中根据具体的应用类型尽量使用已有的中间件产品,不但可以提高系统开发效率,还能保证软件的质量。下图是 Interbank 软件公司开发的跨平台互操作的部分 Java 中间件及相关的层。

Page 97: 第 9 章  面向对象设计

97

用户管理

帐户管理工作流管理

Java 虚拟机 Web 浏览器

TCP/IP

专用应用层

通用应用层中间件层

系统软件层

Java.applet Java.awt Java.rmi

抽象视窗调试工具

远程消息请求

Page 98: 第 9 章  面向对象设计

98

② 定义子系统之间的关系 如果子系统的内容相互有关联,就应该定义子系统之间的依赖关系。依赖关系应该指向子系统的接口,而不应该指向子系统的内容。依赖关系过于密切,则说明一个子系统的变化会导致另一个子系统的变化。解决的办法是: • 重新划分子系统。将子系统的粒度减小,或重新划分子系统的内容。 • 定义子系统的接口,将所有可能的变化形式都反映

到子系统的接口上(像多功能的电源插座一样)。 ③ 定义子系统的接口 接口定义了一些来自外部子系统的访问操作,这些操作由子系统内部的类提供。当有依赖关系直接指向某个子系统时,就有可能表示这个子系统需要提供一个接口。

Page 99: 第 9 章  面向对象设计

99

( 2 )识别对系统构架有重要意义的设计类 ① 根据分析类识别设计类 从分析阶段定义的分析类入手,标识出对系统构架有重要作用的类,将它们定义为设计类。分析类之间的关系自动转化为设计类之间的关系。

帐单帐单

《跟踪》分析模型 设计模型

从分析类导出设计类 这一阶段不要识别太多的类、陷入太多的细节,主要识别是对构架有主要作用的类。大多数设计类要在类设计活动和用例设计活动的基础上识别和精化。

Page 100: 第 9 章  面向对象设计

100

② 识别主动类 考虑系统的并发性需求,识别所需的主动类。例如: • 不同的参与者与系统交互时,会有性能、吞吐量、有效性等不同需求,需要有一个专门的主动对象来管理。 • 需要主动对象支持系统在不同节点上的分布。对于有并发活动的对象,如果系统性能要求不高,可以分配到一个节点上,操作系统靠多任务管理实现逻辑上的并发;如果性能要求高,则将这些对象分配到不同的节点上同时运行。每个节点至少需要一个主动对象,以便处理节点间的相互通信。 • 其他的需求。如:系统的启动、终止。 识别主动类的一个可行方法是利用早期识别的子系统,通过识别子系统内的一个主动类而将该子系统全部分配到某个特定的节点上。任何主动类都可以作为一个在实现过程中被识别的可执行构件的候选。

Page 101: 第 9 章  面向对象设计

101

( 3 )识别通用的设计机制 研究共性需求,并决定如何通过给定的设计和实现技术来处理这些需求。需要处理的需求通常与以下问题有关: • 持久性 • 对象分布的透明性(客户对象不必知道要访问的对象驻留在哪里) • 安全特征 • 错误监测与恢复 • 事务管理 例如:用于处理持久性的设计机制: “ ” “ ”某些对象(如 账单 、 订单 )需持久存在。要处理这样的要求,可以使用对象数据库管理系统、关系数据库管理系统或者普通的二进制文件。哪一个方法最好?

Page 102: 第 9 章  面向对象设计

102

取决于对象需要的访问方式和更新方式,以及哪一种方法更易实现和升级。关系数据库提供较好的表格数据处理性能,而对象数据库则提供较好的复杂对象结构处理性能。关系数据库系统比对象数据库系统更成熟,但建造系统的代价较高,而且在将来当对象数据库足够成熟时肯定需要更新成对象数据库。无论选择哪种方案,设计人员需要把该问题的设计机制写成文档。 以上是构架设计中创建设计模型的活动。下图是

ATM 系统设计模型的部分构架视图。 构架设计还有另外一个活动,即产生实施模型。

Page 103: 第 9 章  面向对象设计

103

在 ATM 系统中,用于实现“取款”用例的子系统是“ ATM 接口”、“事务管理”和“帐户管理”,它们对构架很重要。同时,还有“客户管理”、“事务管理”和“帐户管理” 3 个主动类也隐含在设计模型的构架视图中。 ATM 系统设计模型的构架视图如下图所示:

储户《子系统》ATM 接口 《子系统》事务管理 《子系统》帐户管理

取款

分发转帐 存款

帐单

ATM 系统设计模型的构架视图(部分)

Page 104: 第 9 章  面向对象设计

104

• 创建实施模型的构架视图 实际上是构造系统的物理模型。即根据相互连接的节点定义实际的系统构架。这些节点是软件构件能够在其上运行的硬件单元。需要确定哪些类是主动类(或者考虑包),这些软件元素如何通信、同步和共享信息,以便确定构架模式。同时,将这些软件元素分配到实施模型的节点上。一般使用 UML 的配置图描述系统硬件的物理拓扑结构以及在此结构上分布的软件元素。

Page 105: 第 9 章  面向对象设计

105

在 ATM 系统中,实施模型定义了 3 个节点: 储户通过“ ATM客户机”结点访问系统,该节点通过访问“ ATM 应用服务器”来执行事务。 “ATM 应用服务器”又利用“ ATM 数据服务器”对帐户执行具体的事务。 定义这些节点时,一般将子系统作为一个整体分配在一个节点上,“ ATM 接口”子系统分配在“ ATM客户机”节点上,“事务管理”子系统分配在“ ATM 应用服务器”上,“帐户管理”子系统分配在“ ATM 数据服务器”上。这些子系统中的每个主动类都部署在相应节点

上,如下图所示。

Page 106: 第 9 章  面向对象设计

106

储户ATM 客 户机 ATM 应 用 服 务器

ATM 数 据 服 务器

Internet

局域网

ATM 系统实施模型的构架视图

ATM 接口 事务管理

账户管理

Page 107: 第 9 章  面向对象设计

107

2 、用例实现 - 设计 系统分析阶段有一个阶段产品是用例实现 - 分析,从分析角度详细描述一个用例要完成什么工作。而设计阶段考虑的是用例如何完成它的工作,有哪些设计对象或子系统参与,这些对象或子系统及其接口有什么样的操作需求等等。 下图显示了用例设计的输入和结果。分析模型中相应的用例实现 - 分析是这个活动的基本输入。

Page 108: 第 9 章  面向对象设计

108

用例实现 - 设计的输入和结果

需求补充

概要设计类

用例模型

分析模型概要子系统

概要接口

用例设计

用例设计师 用例实现 - 设计

设计模型

实施模型

Page 109: 第 9 章  面向对象设计

109

( 1 )识别所参与的设计类 分析阶段的用例实现 - 分析说明了参与实现用例的分析类,而设计阶段的用例实现 - 设计跟踪依赖于用例实现 - 分析。设计人员需检查每个用例的功能,考虑这些功能依赖于当前的设计类能否实现,对于特殊需求是否有合适的设计类来实现。同时为每个用例画一张类图(可以将分析模型中的类图作为输入),描述实现用例的设计类及类之间的关系。 当设计这些分析类时,会确定和导出更多应用于实现环境的精细化后的设计类。例如,设计出纳员接口类时,确定和导出了显示类、数字键盘类、读卡机类和客户管理类。 由分析类导出的设计类如下图所示。粗线方框标识的为主动类,主动类实例化的主动对象拥有一个进程或线程并能初始化控制活动。

Page 110: 第 9 章  面向对象设计

110

分发出纳接口 取款 帐户

显示数字键盘 客户管理

读卡机 分配输送器分配传感器点钞机 取款

事务管理帐户其它永久类

帐户管理

《跟踪》《跟踪》 《跟踪》《跟踪》

设计模型的设计类跟踪到分析模型的分析类

Page 111: 第 9 章  面向对象设计

111

( 2 )描述设计对象间的交互 对于比较复杂的用例要详细描述设计对象之间的交互活动。一般用 UML 的顺序图来描述。在细化顺序图时,会发现分析中无法考虑的问题,如:结点之间连接的超时处理,参与者可能提供的错误输入,由中间件、系统软件或硬件产生的错误消息等。 ( 3 )识别参与的子系统和接口 一般将用例设计成类及对象的协作。然而可以在高层用子系统或子系统接口设计一个用例,即参与交互的对象是子系统,交互的消息是在子系统之间传递。(见下图)。 如果一个子系统提供多个接口时,需要区分哪个接口使用哪个消息。下图还显示了参与“取款”用例实现的子系统和接口。 ( 4 )设计用例的非功能性需求并进行实现约束说明。

Page 112: 第 9 章  面向对象设计

112

图 (a) 中的生命线代表子系统。图 (b、 c) 代表这些子系统内部的设计,显示子系统内部的元素是如何接收和发送消息的。图 (a) 可以在图 (b、 c)之前设计。

储户《子系统》ATM 接口 《子系统》事务管理 《子系统》帐户管理

取款

分发

帐单

(b) (a) (c)

参与“取款”用例实现的子系统和接口

Page 113: 第 9 章  面向对象设计

113

3 、设计一个类 设计每个类的内容,包括:操作、属性、所涉及的关系、实现操作的方法 、状态、特殊需求等等。

概要设计类

概要接口

用例实现 - 设计

完整的分析类

设计一个类

构件设计师

完整的设计类

设计一个类的输入和结果

……

Page 114: 第 9 章  面向对象设计

114

( 1 )构造设计类 当给定某个接口作为输入时,直接指定某个设计类来提供这个接口。当给定一个或多个分析类作为输入时,针对分析类的 3 种构造型来构造设计类。 边界类应该转化为用户操作界面。设计的技术依赖于所采用的特定的接口技术。如 Visual Basic环境中,实现边界类实际上对应为 Form 类或其它可视化控件类。 表示持久信息的实体类的设计涉及到特定的数据库技术,创建用来将对象映射成数据表的设计类。 控制类的设计比较难,因为它封装了时序、与其它对象的协调,有时是纯粹的业务逻辑。在多层的结构中,控制类被分类,将业务逻辑放在应用层,将事务处理、对象控制和协调放在中间件层。

Page 115: 第 9 章  面向对象设计

115

( 2)定义设计类的属性 用编程语言的语法来描述属性。通用的指导方针: • 分析类中的一个属性在设计类中可能被分解为多个,减小属性的表示粒度有利于实现与复用。 • 用编程语言来约束可用的属性类型。 • 定义属性类型时,尽可能使用已有的类型。 • 单个的属性实例不能被多个设计对象共享。如果需要共享则将该属性定义成一个单独的类。 • 如果一个设计类因为其属性的原因变得复杂而难以理解,则将一些属性分离出来形成一个新的类。 • 类的属性结构尽量简单,尽可能不使用复杂的数据结构。

Page 116: 第 9 章  面向对象设计

116

( 3 )定义设计类的操作与方法 识别设计类的操作,并用编程语言的语法加以描述,同时说明操作的可见性。考虑以下几方面: • 跟踪对应的分析类的职责。 • 阅读分析类的非功能需求说明,添加一些操作。 • 确定设计类的接口应提供的操作。 • 检查设计类在每个用例实现 - 设计中的角色,补充一些操作。 在设计过程中,方法是用来详细说明操作是如何实现的,即一个操作的算法。方法可用自然语言或伪码来详细说明。如果有设计工具支持,可直接用编程语言说明。

Page 117: 第 9 章  面向对象设计

117

( 4 )定义设计类的关系 设计人员应研究顺序图中对象间的交互,以确定设计类之间的关系。同时应该跟踪对应的分析类之间的关系,可以细化出设计类之间的一到多个关系(如选择最佳检索路径而建立的关系)。 关联关系要使得相互关联的类知道对方,即每个类都要存储对方的一个指针或引用。在编程语言的支持下,设计关联的多重性、角色、关联类等特性。 泛化关系可提高软件的开发效率,跟踪分析类之间的泛化关系产生设计类之间的泛化。但避免过度泛化,可用关联和聚合替代,以提高程序的可理解性。 类之间的关系数目应该最小化!

Page 118: 第 9 章  面向对象设计

118

( 5 )描述设计类的状态 某些对象的行为是由状态控制的,当接收到一个消息时,对象根据所处的状态来选择相应的操作行为。因此用状态图描述一个设计类的不同状态转移就很有意义,对该类的实现、测试也有帮助。 ( 6 )处理设计类的特殊需求 整个系统的公共特殊需求在构架设计师已经描述,这里主要描述一个设计类需要特别设计的非功能性需求。例如,需要从多个结点来访问“账单”对象,“账单”不是一个主动类,但它必须设计得适合于分布系统。可通过把“账单”定义成一个支持远程消息请求的抽象

Java 类的子类来实现该类对象的分布。 有些需求的处理需推迟到实现阶段。

Page 119: 第 9 章  面向对象设计

119

4 、设计一个子系统

设计一个子系统的输入和结果

构架描述

概要子系统完整的子系统

完整的接口

设计一个子系统

构件设计师

概要接口

Page 120: 第 9 章  面向对象设计

120

设计一个子系统的目的是: • 确保该子系统尽可能的独立与别的子系统。 力图将对其它子系统或接口的依赖关系降低到最小限度,要考虑将这些子系统中那些太依赖于其它子系统的类进行重新定位。 • 确保该子系统提供正确的接口。 • 确保该子系统能正确实现其接口所定义的操作。 对于由该子系统所提供的每一个接口,在该子系统内部必须有能提供该接口的设计类或内部其它子系统。 如:

帐户管理子系统帐单账单

Page 121: 第 9 章  面向对象设计

121

5 、设计小结 RUP 设计的主要结果是设计模型,用来可视化的描述系统的实现。包括以下元素: • 设计子系统以及它们的依赖关系、接口和内容。 • 设计类,包括主动类以及它们的属性、操作、关系和实现性需求。 • 用例实现 -设计,描述了一个用例如何用设计对象的交互来实现。 • 设计模型的构架视图,包括对构架有重要意义的元素。 设计还产生实施模型,描述了系统分布应具备的网络配置。

Page 122: 第 9 章  面向对象设计

122

9.6 RUP 的实现活动 主要目标是:在设计模型和实施模型的基础上,产生实现模型。实现模型模型由构件及其接口组成,包括所有的可执行体,如 ActiveX 构件和 JavaBeans 构件,以及其他类型的构件。 在实现工作流期间,要开发可执行系统的产品,如可执行的构件、文件构件(源代码)、表构件(数据库元素)等。一个构件是系统中一个实际的且可替换的部分,它符合并且提供接口的实现。 若用面向对象程序设计语言来实现构件,则类的实现也很简便,每个设计类对应一个实现中的类,如 C++类或 Java 类。 实现工作不仅是开发源代码,还要对源代码进行单元测试。

Page 123: 第 9 章  面向对象设计

123

实现中的活动:1 、构架实现

构架实现的输入和结果

实施模型 构架的实现

构架设计师设计模型

构架描述设计和实施模型的视图

构架描述实现和实施模型的视图

概要构件可能被映射到结点上

Page 124: 第 9 章  面向对象设计

124

通过以下途径勾画实现模型及其构架: ( 1) 识别对构架有重要意义的可执行型构件。 一个简单方法是为每一个主动类创建一个可执行构件。还可确定与该构件相关的其它文件或二进制构件。见下左图。 ( 2) 在相关的网络配置中将构件映射到结点上。

事务处理《主动类》

事务处理《可执行体》

《跟踪》

设计模型 实现模型

: 应用服务器事务处理

: 应用服务器事务处理构件

结点和它的主动类 结点和它的构件

分配到结点的主动类隐含相应的构件实施

隐含

Page 125: 第 9 章  面向对象设计

125

2 、系统集成

系统集成的输入和结果

补充需求

用例模型

实现模型(后续的构造)

系统集成

系统集成人员 集成构造计划

设计模型

实现模型(以前的构造)

Page 126: 第 9 章  面向对象设计

126

系统集成的主要任务是为前面设计的实现模型规划后续的构造( Structure ,是系统的特定组成部分,通常指系统的一个可执行版本 ) ,即按照可管理的步骤对系统进行增量构造。 规划后续构造的准则: ( 1 )每步构造应该实现一个完整的用例,因为测试一个完整的用例比测试它的片断要容易。 ( 2)构造的一次迭代不能包括太多的新增构件。可用

“ ”桩 代替一些构件,使新构件的数量最小化。 ( 3)每个构造应以先前的构造为基础,初始的构造应该在较低的层次上开始,如中间件和系统软件层。随后逐步将构造扩展到通用应用层和专用应用层。

Page 127: 第 9 章  面向对象设计

127

系统集成人员根据以上准则制定构造计划,构造计划为每个构造做如下描述: • 说明在构造中要实现的功能。 • 实现模型在哪些部分受到该构造的影响。 构件工程师可按照计划在当前的构造中实现子系统或构件,并对它们进行单元测试,然后将实现的子系统移交给系统集成人员进行集成。 如果对构造做了仔细的规划,构造集成就比较容易。通过汇集合适版本的实现子系统和构件,对它们进行编译、链接,就可以得到一个构造。在后续的构造创建之前,对前面的每个构造进行集成测试。这样可尽早地产生系统的一个可执行版本并尽早地发现系统级的错误。

Page 128: 第 9 章  面向对象设计

128实现一个子系统的输入和结果

集成构造计划

设计子系统

实现子系统

接口

实现一个子系统

构件设计师

接口

(为某个构造而实现)

(为某个构造而实现)

构架描述(实现模型视图)

(已完成设计)

(已完成设计)

3 、实现一个子系统 目的是确保一个子系统履行它在每个构造中的角色。

Page 129: 第 9 章  面向对象设计

129

《设计子系统》 《实现子系统》

《文件》

《文件》

设计模型 实现模型

《跟踪》

当前构造所需要的类

虽然子系统的内容在设计时确定,随着实现模型的进一步完善,子系统仍需要包括以下两方面的精化:( 1 )当前构造所需要的设计子系统内的每个类应该由实现子系统中的构件来实现。

通过构件来实现在某个构造中所需的设计类

Page 130: 第 9 章  面向对象设计

130

( 2 )当前构造所需要的设计子系统所提供的每个接口也应该由实现子系统提供,即实现子系统必须包含提供该接口的构件。实现接口的构件必须实现接口所定义的全部操作。《设计子系统》 《实现子系统》

《文件》

《文件》

设计模型 实现模型

《跟踪》

当前构造所需的接口 β 实现子系统提供 β

该构件实现接口 β

Page 131: 第 9 章  面向对象设计

131

4 、实现一个类 目的是实现构件中的设计类。一个构件可能有多个设计类和接口。

实现一个类的输入和结果

实现一个类

构件设计师

接口

(已完成设计)

(由设计类提供)

设计类

构件(已实现)

Page 132: 第 9 章  面向对象设计

132

本活动包含下列任务: ( 1) 建立文件构件 由于实现设计类的源代码存放在文件构件中,因此必须创建文件构件并考虑其作用域。在一个文件中存放几个设计类是很正常的,但应注意使用的语言和平台对文件构件的限制。如,使用 Java语言时,要求为每个类

“创建一个 . Java”格式的文件构件。 ( 2)将设计类及其关系生成源代码 设计人员根据设计类的属性定义、操作算法说明和接口说明(这些已经用自然语言或伪码描述了)生成设计类的源代码。对于类的关联和聚合的实现方法取决于

“ ”所用的编程语言。如用 引用 实现关联,这个引用将作为引用对象的一个属性,属性名就是关联另一端的角色名。关联另一端的重数决定属性类型是一个简单指针或是一个指针集合。

Page 133: 第 9 章  面向对象设计

133

( 3)实现设计类的操作 设计类中的每一个操作都应该实现,如果操作是抽象的,这个操作应该由该类的子类来实现。 “ ”操作的实现一般用术语 方法 表示。在实际存在的文件构件中,方法定义的实例有: Java中的method、

Visual Basic中的method、 C++中的member function。

实现设计类的操作包括选择合适的算法和支持的数据结构,这些在设计阶段用自然语言或伪码描述了,实现阶段只是将它们翻译为源代码。 ( 4) 确保构件提供与它所实现的设计类相同的接口。

Page 134: 第 9 章  面向对象设计

134

单元测试的输入和结果

执行单元测试

构件设计师

接口构件

(经过单元测试的构件)

构件(已实现)

该活动的主要任务包括:( 1 “ ”)使用 黑盒法 ,根据规格说明验证构件外观上可观察的行为。( 2 “ ”)使用 白盒法 验证构件的内部工作。

5 、执行单元测试 把已实现的构件作为个体单元进行测试。

Page 135: 第 9 章  面向对象设计

135

6 、实现小结 实现阶段的主要结果是实现包含以下元素的实现模型: ( 1)实现子系统及其依赖关系、接口和内容。 ( 2)构件(包括文件构件和可执行构件)以及它们之间的依赖。构件经过了单元测试。 ( 3) 实现模型的构架视图,包括对构架有重要意义的元素。 当可执行构件被映射到结点时,实现还对实施模型的构架视图进行了精化和改进。 实现模型是测试活动的主要输入。

Page 136: 第 9 章  面向对象设计

136

习题 1 、请比较 OO 设计原则与传统方法设计原则的不同。 2、 OO 系统设计包括哪些活动?解释这些活动的主要思想。 3 、为什么划分子系统往往按层次来划分?解决子系统间的通 信,常用什么体系结构? 4 、什么是中间件?有什么作用? 5 、怎样确定系统的并发性?如何分配子系统? 6 、数据管理设计要考虑什么内容? 7 、对象设计与系统设计有何区别?对象设计有哪些具体活动? 8 、设计模式是什么?使用设计模式有什么作用?试举例说明 设计模式的应用。 9 、 RUP 的设计和实现有哪些活动?你认为有何特点?10 、应用问题:根据某个具体实例,回答系统设计、对象设计 方面的有关问题。