Download presentation
Presentation is loading. Please wait.
1
GRASP:设计带职责的对象 徐迎晓 复旦大学软件学院
2
设计带职责的对象 交互设计和职责分配的质量决定系统的质量 好的面向对象设计原则可用于交互图设计和职责分配—GRASP模式 可维护性 可理解性
可重用性 可扩展性 好的面向对象设计原则可用于交互图设计和职责分配—GRASP模式
3
GRASP:通用职责分配软件模式 将职责分配给对象的基本原则
General Responsibility Assignment Software Patterns 将职责分配给对象的基本原则
4
GRASP:设计带职责的对象 五个GRASP模式 应用GRASP实现Process Sale用例 可见性设计 类图设计
5
五个GRASP模式 . Low Coupling . High Cohesion . Controller . Creator
. Information Expert
6
模式: Low Coupling Problem:如何降低依赖、减少改变的影响,增加重用性? Solution:分配职责时使耦合低
7
原理: 高耦合带来的问题 耦合表明两个类之间连接的强度,一个类是否依靠其他类 A具有低耦合,则A不依赖很多其他的类
其他类改变则可能自己也必须改变 单独(无其他类时)难以理解 由于需要很多其他类,难以重用
9
举例 we have a need to create a Payment instance and associate it with the Sale. 谁来创建Payment 实例?
13
两种都假定Sale和Payment之间有关联,register创建则增加关联,不好
14
讨论 低藕合模式常与专家、高内聚一起使用 是基本目标,所有设计决策中都要考虑 中等层度的藕合是正常和必需的(对象之间需要连接以便协作)
优点:不受其他组件变化的影响,易于理解,易于重用
15
类X和类Y之间常见的藕合 X有属性引用Y的实例 X对象调用Y对象的服务 X的方法引用Y的实例(参数,局部变量,返回值) X是Y直接或间接子类
16
But 对稳定元素的藕合和普遍的元素藕合很少会有问题
如类库
17
五个GRASP模式 . Low Coupling . High Cohesion . Controller . Creator
. Information Expert
18
模式:Hign Cohesion Problem:如何使复杂性可管理? Solution:分配职责时使内聚度高
19
内聚:类的职责如何紧密关联 类或子系统等若职责紧密关联,且没有大量的工作,则具有高内聚度 高内聚则方法的数量相对较少
若task is large,则与其他对象协作 低内聚说明类做了很多无关的事情,或做了太多的事情 低内聚的问题:难以理解,重用,维护,易受变化的影响 修改:在进行测试和发布时,类通常是最基本的单元,如果其粒度太粗,在一个类中设计了大量的操作,则会有大量使用者使用该类的不同操作,但为了某一类使用者修改了某几个操作时,整个类就要重新发布,并有可能会影响到使用另几个操作的另一类使用者。 查找:此外,如果类中操作太多,类的使用者要从一长串操作中挑选自己所需要的也是很吃力的
20
不独立使用,而是与专家、低藕合模式一起使用
评估所有设计决策的一个准则 Register做很多系统操作,下图哪个好?
22
RDB-RPC-Interface class
responsible for interacting with relational databases and for handling remote procedure calls (two vastly different functional areas) 几个不同内聚度的例子 --Very low cohesion 一个类独自负责不同功能区域的很多事情A class is solely responsible for many things in very different functional areas
23
RDBInterface class completely responsible for interacting with relational databases. hundreds or thousands of methods(all related) A lot of supporting code Low cohesion A class has sole responsibility for a complex task in one functional area.
24
RDBInterface class Only partially responsible for interacting with relational databases interacts with a dozen other classes related to RDB access High cohesion 一个功能区域,职责中等(与其他类协作完成任务)A class has moderate responsibilities in one functional area and collaborates with other classes to fulfill tasks.
25
Company class completely responsible for (a) knowing its employees and (b) knowing its financial information. total number of public methods is small the amount of supporting code is small Moderate cohesion (类具有轻量级的、独立的职责,职责覆盖几个不同的功能区域,职责相互之间的关系不大,但都与类的概念逻辑相关) A class has lightweight and sole responsibilities in a few different areas that are logically related to the class concept, but not to each other
26
可低内聚的场合 grouping of responsibilities or code into one class or component to simplify maintenance by one person 只有1,2个SQL专家,对OO不熟 software architect may decide to group all the SQL statements into one class, RDBOperations
27
可低内聚的场合2 distributed server objects
less remote calls, and better performance. 远程服务器对象要fewer and larger, less cohesive,为很多操作提供接口 远程操作粗粒度,以便一次remote operation call可以做或请求更多的工作 Example 1: remote object with three fine-grained operations setName, setSalary, and setHireDate Example 2: one remote operation setData which receives a set of data Which is better? Solution 2 better create fewer and larger, less cohesive server objects that provide an interface for many operations remote operations are made more coarse-grained in order to do or request more work in remote operation call
28
五个GRASP模式 . Low Coupling . High Cohesion . Controller . Creator
. Information Expert
29
模式:Controller Problem:谁负责处理系统输入事件? Solution:将处理系统输入事件的职责分配给代表以下的类:
代表整个系统、设备或子系统-外观Controller(facade controller) 代表整个业务过程或组织(facade controller) 代表现实世界中可能执行该任务的角色(role controller) 代表系统事件发生的用例场景,(use-case or session controller)
30
Problem: Solution (advice):
The UI & domain layers should be loosely coupled Which object should coordinate msgs between the UI and other domain objects? Solution (advice): Assign responsibility to an object representing.. The ‘system’ or a ‘root’ object (Store, Bank) A device/subsystem (AccountingSystem, BankATM) A use case scenario (GameHandler) ‘Single channel of communication between layers’
31
“window,” “applet,” “widget,” “view,” and “document”不是控制器,而是将事件发往控制器
系统操作在设计时通常交给控制器
32
Controller是从界面层进入doamin layer的门面
33
可供选择的 代表整个系统、设备或子系统 代表整个业务过程或组织 代表现实世界中可能执行该任务的角色 代表系统事件发生的用例场景
Register, POSSystem 代表整个业务过程或组织 Store 代表现实世界中可能执行该任务的角色 Cashier 代表系统事件发生的用例场景 ProcessSaleHandler
34
对应前面四种
35
整个系统、设备或子系统
36
系统事件发生的用例场景
37
讨论 大多数系统接受外部输入事件(GUI,传感器信号,…) 一个用例中所有系统事件用一个控制器处理,不同用例用不同控制器
控制器通常将将需要做的工作委托给其他对象,自己并不做很多工作
38
优点 但要防止控制器过于臃肿 业务处理由业务对象完成,而不是把图形界面作为控制器,这样可重用性高
将控制器的职责委托给各个领域类,支持逻辑重用 但要防止控制器过于臃肿 可用多个控制器 可用角色控制器或用例控制器,而不是facade controller
40
图形界面
41
不好的做法
42
五个GRASP模式 . Low Coupling . High Cohesion . Controller . Creator
. Information Expert
43
模式:Creator Problem:谁负责创建新的对象? Solution: 如果满足以下条件之一,则将创建类A的实例的职责分配给类B
B聚合A对象 B包含A对象 B contains A objects. B记录A对象(前三个最常用) B紧密使用A对象 B具有创建A对象时需要传入的初始化数据 B是A的Creator 如果有多个类满足条件,优先选择聚合或包含关系 Aggregate aggregates Part, Container contains Content, and Recorder records Recorded
44
Name: Creator Problem: Who creates an A? Solution: (this can be viewed as advice) Assign class B the responsibility to create an instance of class A if one of these is true (the more the better): B "contains" or compositely aggregates A. B records A. B closely uses A. B has the initializing data for A.
45
举例 谁负责创建SalesLineltem对象? 应该Sales LineItem
48
讨论 低耦合 简明,封装 But 若出于性能考虑使用回收的实例,或根据外部的值从一组类似的类中挑选一个创建实例 对依赖的维护少 可重用性高
建议将创建工作交给helper class----Factory,而不使用本模式
49
五个GRASP模式 . Low Coupling . High Cohesion . Controller . Creator
. Information Expert
50
模式:Information Expert
Problem OOD中分配职责最基本的原则是什么? Solution将职责分配给Information Expert (拥有完成该职责所需要信息的类)
51
1.如果设计模型存在相关的类,则到领域模型中查找
2. 否则,查看领域模型,应用或扩展领域模型得到相应的设计类
52
画交互图时碰到的问题:计算总价(总计,grand total)的职责分配给谁?
检查领域模型或设计模型
53
计算总价需要知道所有销售条目(SalesLineltem)及其各个条目的总价(subtotals)之和
Sale包含SalesLineltem这些信息——所以计算总价(grand total)的职责分配给Sale。 SalesLineItem 子销售
54
此时对交互图和类图可以增加哪些内容?
56
各个条目的总价(subtotals.)分配给谁?
需要知道数量SalesLineltem.quantity和单价ProductSpecification.price 分配给SalesLineltem
57
交互图和类图增加什么内容?
59
获取单价的职责分配给谁? ProductSpecification包含该信息
60
最终通过Information Expert模式得到的职责分配
为了能得到总价的信息:
61
最终通过Information Expert模式得到的协作图
62
讨论模式Information Expert
来自直觉--对象做的事情总是与其拥有的信息有关 常需要多个对象协作 But 影响耦合和内聚时,可能不使用Expert 将销售保存的数据库的职责分配给谁?
63
优点 低耦合--对象用自己的信息,封装性好 高内聚-行为分布在具有所需信息的类上,易于理解和维护
64
GRASP:设计带职责的对象 五个GRASP模式 应用GRASP实现Process Sale用例 可见性设计 类图设计
65
应用GRASP实现Process Sale 用例
用例实现是UP设计模型的一部分 建议并行画类图和交互图 初始阶段:设计模型和用例实现通常不做,因为涉及详细的设计决策,而初始阶段尚不成熟 细化阶段:创建体系结构上重要的或风险较大的用例实现 构造阶段:为剩下的设计问题创建用例实现
67
先为每个系统操作创建独立的图,将每个系统事件作为起始消息 若图太复杂,分解成小的图 使用系统操作合约和用例描述作为起点,使用GRASP等模式
可以直接从UC文本设计用例实现,但对某些系统操作,合约可以提供更多的细节和描述 如果我们以前跳过Contract的创建,仍旧可以通过用例文本创建交互图,但合约将相关信息以workable format组织和隔离出来,并且可鼓励在分析阶段进行这方面研究工作
69
Controller 协作图:每个系统事件要画一个
70
顺序图:可在一张图上
71
太长的也可每个系统事件 分开
73
makeNewSale
75
通过应用GRASP控制器模式,交互图以向Register软件对象发送makeNewSale消息开始
1.选择控制器类 This Register is a software object in the Design Model. not a real physical register is a software abstraction It’s name was chosen to lower the representational gap between our concept of the domain and the software
76
Fig. 18.6
78
enterItem UC 3. Cashier enters item identifier.
后面:选择控制器,显示Item Description和Price, 创建SalesLineItem,查找ProductSpecification, 从数据库获取ProductSpecification,多对象 UC 3. Cashier enters item identifier. 4. System records sale line item and presents item description, price, and running total.
80
选择控制器,显示Item Description和Price, 创建SalesLineItem,查找ProductSpecification, 从数据库获取ProductSpecification,多对象 1. 选择控制器 2. 显示ItemDescription and price(ignored, Model/View分离)---domain object不需要和图形界面通信 3. 创建SaleLineItem 4. enterItem从界面中得到的参数是id和qty,而 向sale发消息创建SalesLineItem时,SalesItem中保存的是spec和qty(SalesItem需要和spec关联),所以需要给定id得到spec.下下页: 5.从数据库获取ProductSpecification(信息提取延迟到以后设计) 6. 发往多对象的消息默认的解释是发向容器中的每个对象, 但对于find, add等明显的操作解释为对容器的操作
81
给定id得到spec,Product Catalog保存了所有id和description对应关系 信息专家-分配给Product Catalog
82
谁向ProductCatalog发送消息要ProductSpecification?
假设Register和ProductCatalog实例在UC启动时创建,二者之间存在永久connection,则可以由Register发送,见该图 ---这引入另一个概念:Visibility,A向B发送消息,A必须可见B
83
Fig. 18.7
85
endSale Cashier repeats steps 3-4 until indicates done. 指示重复
Cashier按下按钮指示结束Sale时发生 1.选择控制器 2. 设置Sale.isComplete属性 3. 显示信息 4. 计算Total Cashier repeats steps 3-4 until indicates done. 指示重复 5. System presents total with taxes calculated.
86
endSale 1.选择控制器 2. 设置Sale.isComplete属性---专家
88
Fig. 18.9
89
3. 显示信息---模型视图分离 4. 计算Total
90
{}中为getSubtotal()实现的半形式化描述,aSLI并未正式定义,但开发者可以理解是一个SaleLineItem实例
91
Fig & 18.12
93
makePayment 后面:选择控制器,创建Payment,记录已完成的Sale,计算余额 8. System logs completed sale and sends sale and payment information to the external Accounting system and Inventory system
95
Make Payment: Creating the Payment
选择控制器,创建Payment * , 记录已完成的Sale,计算余额
96
CashTendered实际支付 Fig
97
Make Payment: Logging a Completed Sale
Who is responsible for knowing all the logged sales, and doing the logging? 选择控制器,创建Payment , 记录已完成的Sale*,计算余额
98
====需求指出Sale应该放在historical log中
从减少representational gap角度,Store知道所有logged sales是合理的.早期Store职责很少时可这么做 但when the design grows and the Store becomes uncohesive.低内聚,则可以交给SalesLedger(账簿) ------ the contract indicate relating the Sale to the Store. This is an example where the postconditions may not be what we want to actually achieve in the design. 领域模型也可跟着进化 ====E======= the requirements state that the sale should be placed in an historical log By the goal of low representational gap in the software design, it is reasonable for a Store to know all the logged sales
99
这里仍旧先由Store创建
100
Fig
101
makePayment: Calculating the Balance
The Process Sale use case implies that the balance due from a payment be printed on a receipt and displayed somehow. 选择控制器,创建Payment , 记录已完成的Sale,计算余额*
102
7a. Paying by cash: 1 收银员输入收取的现金数额 2 系统给出应找的余额,并弹出现金抽屉
3 收银员放入收取的现金,并拿出应找的余额给顾客 4 系统记录现金支付 1. Cashier enters the cash amount tendered. 2. System presents the balance due, and releases the cash drawer. 3. Cashier deposits cash tendered and returns balance in cash to Customer. 4. System records the cash payment.
103
Model-View Separation principle, we should not concern ourselves with how the balance will be displayed or printed
104
首先考虑信息专家,除非是创建者或控制器问题
计算找零,需要知道Sale总金额和payment中现金付款金额(cash tendered) -- Sale and Payment, 若Payment作为信息专家,则payment需要可见Sale,增加了耦合 Object Constraint Language (OCL).
105
Fig
106
startUp 大部分系统都有startUp用例,startUp系统操作最早执行,但在考虑过其他系统操作后才开发。——最后做初始化设计
应用程序如何启动并初始化,依赖于语言和操作系统 常见的设计方法是创建initial domain object——the first software "domain" object created. 负责创建direct child domain objects. Store若作为initial domain object,则负责创建Register 何处创建初始领域对象?——Java main() 方法,也可由factory对象做 The initial domain object does not usually take control if there is a GUI; otherwise, it often does.
107
StartUp的开始 创建初始领域对象——create消息 初始领域对象若控制进程,再画一个交互图发送run或类似消息给初始领域对象
108
control will remain in the UI layer (such
The startUp system operation occurs when a manager powers on the POS system and the software loads. control will remain in the UI layer (such as a Java JFrame) after the initial domain object is created. The initial domain object does not usually take control if there is a GUI; otherwise, it often does.
109
Choosing the Initial Domain Object
初始领域对象的选择:选靠近聚合或容器层次结构的根的类来创建领域对象 1. façade controller 如Register 2. 基本上包含了所有其他对象的对象,如Store 根据对耦合和内聚的影响,考虑用Register还是Store,这里选择Store
110
StartUp启动时对数据库的操作 ProductSpecification实例存储在存储介质如关系数据库或对象数据库中 StartUp时,
若很多,消耗内存和时间太多,可在需要时才装载 对象数据库易于动态将对象装载到内存,关系数据库可留到后面设计
111
StartUp创建其他哪些对象 考虑初始领域对象中要创建哪些对象,加哪些关联 根据以前设计工作(如EnterItem的设计)的需要来定
113
1.1是临时的,最终的设计中当需要ProductSpecification时才从数据库中生成
现实世界和软件世界的差别:当前需求中 软件世界中Store只需要创建一个Register
114
Fig
115
常见设计:Main中创建一个UI对象和领域对象,并将领域对象传递给UI(如上)
UI得到领域对象(Register)后就可以向其发送系统事件消息
116
设计1:将getTotal()放在Register中,UI和领域层低耦合,但Register内聚度低
117
UI需要知道任何与Sale相关的信息时,请求一个当前Sale对象的引用,直接向其发送消息
118
用例实现和UP 用例实现是UP设计模型的一部分 建议并行画类图和交互图
初始阶段:设计模型和用例实现通常不做,因为涉及详细的设计决策,而初始阶段尚不成熟 细化阶段:创建体系结构上重要的或风险较大的用例实现 构造阶段:为剩下的设计问题创建用例实现 MSE here
119
GRASP:设计带职责的对象 五个GRASP模式 应用GRASP实现Process Sale用例 可见性设计 类图设计
120
可见性设计 基本判断: 若对象A向对象B发消息,则B必须对A是可见的
122
四种可见方式 Attribute visibility– B是A的属性 Parameter visibility—B是A的方法的参数
Local visibility—B是A的局部对象(非方法的参数). • Global visibility—B是以某种方式全局可见的
123
属性可见 相对永久,最常见 public class Register { …
private ProductCatalog catalog; }
124
属性可见
125
参数可见 Sale的方法makeLineItem( ), 其中
S1=new SaleLineItem()时使用的spec是由makeLineItem的参数传入的
126
相对临时的可见性,只在方法范围内可见 可见性的第二种常见形式
127
局部可见 相对临时的可见性,只在方法范围内可见 两种常见情况 创建局部实例,并赋给局部变量 将方法执行的返回对象赋给局部变量
129
全局可见 相对永久的可见性 C++全局变量 Singleton pattern
130
GRASP:设计带职责的对象 五个GRASP模式 应用GRASP实现Process Sale用例 可见性设计 类图设计
131
类图设计
132
依据 交互图 软件类和方法 概念模型 类定义的细节 由交互图确定软件类和方法
133
领域模型和设计类图
134
方法
136
Method Name Issues Method Names—create create message不同语言中实现不同
C++,自动或new调用构造器 Java,new 构造器 初始化是常见的activity,通常DCD中可省略 creation-related methods and constructors from a DCD. DCD Design Class Diagrams
137
Method Names—Accessing Methods
getXXX(), setXXX() high noise-to-value ratio,for n attributes, there are 2n uninteresting methods. 通常省略
138
Method Names—Multiobjects
发向多对象的消息,针对的是容器本身(List, Map), 故ProductSpecification类不应该有find() 方法
139
Method Names—Language-Dependent Syntax
生成代码时自动转换成相应的语言格式
140
根据对象的不同,可简单(给开发人员读),可更详细(用工具, 可生成代码,如上图)
141
加上关联
142
和domain model中的类图不完全相同
如domain model中Register and ProductCatalog没有考虑需要关联,而创建交互图时发现Register软件对象根据id查找ProductSpecifications时需要与ProductCatalog连接
144
Adding Dependency Relationships
depict non-attribute visibility between classes; in other words, parameter, global, or locally declared visibility. Register software object向ProductCatalog发送消息时,获得 ProductSpecification 类型的返回值,因此Register 短期局部和ProductSpecifications可见(1) Sale接收makeLineItem消息时,传入的是ProductSpecification参数,和ProductSpecification参数可见(2)
146
visibility marker for attribute or method
current iteration does not have many interesting member details; all attributes are private and all methods public.
147
Rose演示
Similar presentations