创建者(Creator)模式定义
名称:创建者(Creator)
问题:谁创建了A?
解决方案(可被视作建议):如果以下条件之一为真时(越多越好),将创建类A示例的职责分配给类B:
B“包含”或组成聚集了A.
B记录A.
B紧密地使用A.
B具有A的初始化数据.
问题
<span style=”color:#0000ff”>谁来负责创建某些类的新实例?
在面向对象系统中对象的创建是一个最普通的活动
我们需要一个分配创建责任的通用原则</span>
解决方案
<span style=”color:#008000″>将创建类A的实例的责任分配给类B,如果一个或多个下面的条件为真:
B 聚合了(aggregates) A 对象
B包含了(contains)A对象
B记录了(records)A对象的实例
B密切使用(closely uses)A对象
B拥有传递给创建A所需要的初始化数据 ( 因此B是创建A的专家)
B是A对象的创建者</span>
信息专家(Information Expert)模式定义
名称:信息专家(Information Expert)
问题:<span style=”color:#0000ff”>给对象分配职责的基本原则是什么?</span>
解决方案(建议):<span style=”color:#008000″>把职责分配给具有完成该职责所需信息的那个类。</span>
问题
<span style=”color:#000000″>设计模型可能定义成百上千个软件对象,一个应用中也可能需要将成百上千项责任进行分配
</span>目的: 易于理解,维护,扩展和重用
解决方案
<span style=”color:#008000″><span style=”color:#000000″>将责任分配给信息专家,所谓的信息专家是拥有完成责任的信息的类</span>
</span>例子
NextGEN POS 应用,某些类需要了解销售总额
谁负责了解销售总额?
拥有需要的信息的对象类确定总额
需要回答的问题
我们已经有领域模型了,但是我们依然没有设计模型
低耦合(Low Coupling)模式定义
名称:低耦合(Low Coupling)
问题:<span style=”color:#0000ff”>如何减少因变化产生的影响?
</span>解决方案(建议):<span style=”color:#008000″>分配职责以使(不必要的)耦合保持在较低的水平。用该原则对可选方案进行评估。
</span>问题
耦合是对一个元素与其他元素的连接,拥有知识,依赖等关系的强烈程度的度量
高耦合造成:
相关的类的修改造成本地修改
难以理解
难以重用
解决方案
分配责任使得耦合度比较低
控制器(Controller)模式定义
名称:控制器(Controller)
<span style=”color:#000000″>问题:在UI层之上首先接收和协调(“控制”)系统操作的对象是什么?
解决方案(建议):把职责分配给能代表下列选择之一的对象:
代表全部“系统”、“跟对象”、运行软件的设备或主要的子系统
代表发生系统操作的用例场景</span><span style=”color:#008000″>
</span>问题
<span style=”color:#0000ff”>谁应该为处理输入系统事件负责?
一个控制器是一个没有用户界面的对象,负责接收和处理系统事件。控制器定义了系统操作的方法
</span>解决方案
<span style=”color:#008000″>将接收和处理系统事件消息的责任分配给代表下列方面的类:
代表整个系统,设备或子系统
代表用例场景,在该场景中发生系统事件,通常命名为<useCaseName>Handler、 <useCaseName>Coordinator或<useCaseName>Session(用例或会话控制器)</span>
高内聚(High Cohesion)模式定义
名称:高内聚(High Cohesion)
问题:<span style=”color:#0000ff”>怎样使对象保持有内聚、可理解和可管理,同时具有支持低耦合的附加作用?
</span>解决方案(建议):<span style=”color:#008000″>职责分配应该保持高内聚,一次来评估备选方案。</span>
问题
内聚是对一个元素的责任之间的关系是否密切相关和集中的度量
低内聚的类包含了很多无关的事情,或者承担了太多的工作。它们有下面这些的问题:
难以理解
难以重用
难以维护
脆弱; 常常受修改影响
解决方案
责任分配以保证高内聚
多态性(Polymorphism)
问题:
<span style=”color:#0000ff”>如何处理基于类型的选择?
“If-then-else”; “select-case”?
很难方便地扩展有新变化的程序
如何创建可插拔的软件构件?
客户-服务器关系中的可视化构件,如何才能够替换服务器构件,而不对客户端产生影响呢?
</span>解决方案
<span style=”color:#008000″>当相关选择或行为随类型(类)有所不同时,使用多态为变化的行为类型分配职责
</span>多态示例1
NextGen问题:如何支持第三方税金计算器?
它们具有不同技术实现的不同的
TCP socket
SOAP interface
Java RMI interface
基于多态,我们应该为不同的计算器(或计算器适配器)对象自身分配适配的职责,这可以通过多态的getTaxes操作来实现。
多态示例2
Monopoly问题:如何设计不同的方格活动?
不同类型的方格有不同的规则
纯虚构(Pure Fabrication)
问题:
<span style=”color:#0000ff”>面向对象设计依据真实世界中的概念来表示软件类
但是软件并非真实的世界
我们需要通过领域层对象来克服
低内聚或者耦合
低复用
</span>解决方案
<span style=”color:#008000″>对人为制造的类分配一组高内聚的职责,该类并不代表问题领域的概念-虚构的事物,用以支持高内聚、低耦合和复用
</span>纯虚构示例1
为了保存Sale 的实例到关系数据库
基于信息专家的考虑,存在理由将责任分配给Sale 类自己. 但是考虑如下含义:
需要大量的数据库操作,这将使Sale 变得非内聚
Sale 类必须与关系数据库接口耦合,这样增加了耦合
将对象保存到关系数据库中是一个普遍的任务-它可以重用
纯虚构解决了如下设计问题
使Sale保持高内聚和低耦合的良好设计
PersistentStorage类本身是相对内聚的,具有唯一目标,即在持久性存储介质中存储或插入对象。
PersistentStorage类是十分普遍和可复用的对象。
消除了基于专家模式的不良设计,改善了内聚和耦合,增加了潜在的复用性
纯虚构示例2
在当前设计中,由Player滚动所有骰子并且计算点数总和。缺点:
骰子在大量游戏中十分常见,分配给Player,计算总点数的服务将无法用于其他游戏。
如果不再次滚动骰子,则不可能获知当前的骰子总点数。
间接性(Indirection)
问题:
<span style=”color:#0000ff”>为了避免两个或多个事物之间的直接耦合,应该如何分配职责?
如何使对象解耦,以支持低耦合并提高复用性?
</span>解决方案:
<span style=”color:#008000″>将职责分配给中介对象,使其作为其他构件或服务之间的媒介,以避免它们之间的直接耦合
</span>间接性示例
TaxCalculatorAdapter
PersistentStorage
可视为Sale和数据库之间的中介
防止变异(Protected Variations)
问题:
<span style=”color:#0000ff”>如何设计对象,子系统和系统,使其内部的变化或不稳定性不会对其它元素造成不良影响</span>
解决方案:
<span style=”color:#008000″>识别预计变化或不稳定之处,分配职责用以在这些变化之外创建稳定接口</span>
防止变异示例
外部税金计算器问题及其使用多态的解决方案能够描述防止变异
内部对象只与稳定的接口协作,各种适配器实现隐藏了外部系统的变化。
适配器Adapter (GOF)
问题
<span style=”color:#0000ff”>如何解决不相容的接口问题,或者如何为具有不同接口的类似构件提供稳定的接口?
</span>解决方案
<span style=”color:#008000″>通过中介适配器对象,将构件的原来的接口转换为其它接口
</span>多态模式就是该模式的例子
适配器示例1:
POS系统需要支持多重第三方外部服务,其中包括税金计算器、信用卡授权服务、库存系统和财务系统。他们都具有不同的API,而且还无法改变。
上述适配器模式的使用可以视为某些GRASP构造模块的特化:
适配器支持防止变异,因为它通过应用了接口和多态的间接对象,改变了外部接口或第三方软件包。
工厂Factory (GoF)
也称为简单工厂(Simple Factory)或具体工厂(Concrete Factory)
是GoF抽象工厂(Abstract Factory)模式的简化
广泛应用
问题:
<span style=”color:#0000ff”>当有特殊考虑(例如存在复杂创建逻辑,为了改良内聚而分离创建职责等)时,应该由谁来负责创建对象?</span>
解决方案:
<span style=”color:#008000″>创建称为工厂的纯虚构对象来处理这些创建职责
</span>示例
在适配器模式中,对外部服务有不同的接口,那么是谁创建了这些适配器,并且如何决定创建哪种类的适配器?
选择领域对象来创建适配器不能支持关注分离的目标,也降低了内聚。
定义纯虚构的“工厂”对象来创建对象。
单实例类Singleton
问题:
<span style=”color:#0000ff”>只有唯一实例的类即为“单实例类”。对象需要全局可见性和单点访问。
</span>解决方案:
<span style=”color:#008000″>对类定义静态方法用以返回单实例。</span>
单实例示例
谁来创建工厂ServiceFactory自身?
如何访问工厂?
将实例作为一个参数进行传递
对那些需要可见性的对象在初始化时赋予一个持久引用
策略(Strategy )
问题:
<span style=”color:#0000ff”>如何设计变化但相关的算法或政策?如何设计才能使这些算法或政策具有可变更的能力
</span>解决方案:
<span style=”color:#008000″>在单独的类中分别定义每种算法/政策/策略,并且使其具有共同接口</span>
策略示例
提供更为复杂的定价逻辑,例如商店在某天的折扣,老年人折扣等
组合模式(Composite Pattern)
问题:
<span style=”color:#0000ff”>如何能够像处理非组合(原子)对象一样,(多态地)处理一组对象或具有组合结构的对象呢?</span>
解决方案:
<span style=”color:#008000″>定义组合和原子对象的类,以使他们实现相同的接口</span>
组合示例
如何处理多个互相冲突的定价策略?
老年人20% 的折扣
对于购物金额满400元的优先客户给与折15%的折扣
在星期一,购物金额满500元的享受50元的折扣
买一罐印度大吉岭茶, 则所有购买物品都享受15%的折扣
解决冲突的策略
对顾客最有利或者对商场最有利
定价策略可能与产品类型相关,也可能与客户类型相关,这意味着:
StrategyFactory 必须知道客户和产品的类型
外观(Fa?ade)
问题:
<span style=”color:#0000ff”>对一组完全不同的实现或接口(例如子系统中的实现和接口)需要公共、统一的接口。可能会与子系统内部的大量事物产生耦合,或者子系统的实现可能会改变。怎么办?</span>
解决方案:
<span style=”color:#008000″>定义对子系统的单一的接触点- 用一个外观对象包装子系统。外观对象提供了一个唯一的统一接口,它负责与子系统的组件进行协作</span>
外观示例
可插拔的业务规则
创建新销售时,可能要识别该销售是否以礼券方式进行支付。商店可能会规定如果使用礼券只可以购买一件商品。
此时enterItem在完成第一次操作后应该变为无效。
如果销售使用礼券支付,在对该顾客找零时,除了礼券之外所有其它支付类型的找零都应该置为无效。
例如,如果收银员请求现金找零或更新顾客在其商店帐户上的积分时,这些请求都应该判断为无效
….
如何设计一个软件对目前的软件组件影响最小呢?
观察者/发布-订阅/委派事件模型
Observer/Publish-Subscribe/Delegation Event Model
名称:观察者(Observer)(发布-订阅(Publish-Subscribe))
问题:
<span style=”color:#0000ff”>不同类型的订阅者对象关注于发布者对象的状态变化或事件,并且想要在发布者产生事件时以自己独特的方式作出反应。此外,发布者想要保持与订阅者的低耦合。如何对此进行设计呢?</span>
解决方案:
<span style=”color:#008000″>定义 “订阅” or “聆听者” 接口。订阅者实现该接口。发布者可以动态的注册关注某事件的订阅者,并在事件发生时通知它们。</span>
观察者示例
添加当总价变化时,GUI窗口能够更新其显示的能力
用例图
活动图
状态图
系统顺序图
类图
时序图