好书整理系列之-设计模式:可复用面向对象软件的基础 5


第5章行为模式
行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述
它们之间的通信模式。这些模式刻划了在运行时难以跟踪的复杂的控制流。它们将你的注意
力从控制流转移到对象间的联系方式上来。
行为类模式使用继承机制在类间分派行为。本章包括两个这样的模式。其中Te m p l a t e
M e t h o d(5 . 1 0)较为简单和常用。模板方法是一个算法的抽象定义,它逐步地定义该算法,
每一步调用一个抽象操作或一个原语操作,子类定义抽象操作以具体实现该算法。另一种行
为类模式是I n t e r p r e t e r(5 . 3)。它将一个文法表示为一个类层次,并实现一个解释器作为这些
类的实例上的一个操作。
行为对象模式使用对象复合而不是继承。一些行为对象模式描述了一组对等的对象怎样
相互协作以完成其中任一个对象都无法单独完成的任务。这里一个重要的问题是对等的对象
如何互相了解对方。对等对象可以保持显式的对对方的引用,但那会增加它们的耦合度。在
极端情况下,每一个对象都要了解所有其他的对象。M e d i a t o r(5 . 5)在对等对象间引入一个
m e d i a t o r对象以避免这种情况的出现。m e d i a t o r提供了松耦合所需的间接性。
Chain of Responsibility(5.1)提供更松的耦合。它让你通过一条候选对象链隐式的向一个对
象发送请求。根据运行时刻情况任一候选者都可以响应相应的请求。候选者的数目是任意的,
你可以在运行时刻决定哪些候选者参与到链中。
O b s e r v e r ( 5 . 7 )模式定义并保持对象间的依赖关系。典型的O b s e r v e r的例子是Smalltalk 中的
模型/视图/控制器,其中一旦模型的状态发生变化,模型的所有视图都会得到通知。
其他的行为对象模式常将行为封装在一个对象中并将请求指派给它。S t r a t e g y ( 5 . 9 )模式将
算法封装在对象中,这样可以方便地指定和改变一个对象所使用的算法。C o m m a n d ( 5 . 2 )模式
将请求封装在对象中,这样它就可作为参数来传递,也可以被存储在历史列表里,或者以其
他方式使用。S t a t e ( 5 . 8 )模式封装一个对象的状态,使得当这个对象的状态对象变化时,该对
象可改变它的行为。Vi s i t o r ( 5 . 11 )封装分布于多个类之间的行为,而I t e r a t o r ( 5 . 4 )则抽象了访问
和遍历一个集合中的对象的方式。
5.1 CHAIN OF RESPONSIBILITY(职责链)-对象行为型模式
1. 意图
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这
些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
2. 动机
考虑一个图形用户界面中的上下文有关的帮助机制。用户在界面的任一部分上点击就可
以得到帮助信息,所提供的帮助依赖于点击的是界面的哪一部分以及其上下文。例如,对话
框中的按钮的帮助信息就可能和主窗口中类似的按钮不同。如果对那一部分界面没有特定的
帮助信息,那么帮助系统应该显示一个关于当前上下文的较一般的帮助信息-比如说,整个
对话框。
因此很自然地,应根据普遍性( g e n e r a l i t y )即从最特殊到最普遍的顺序来组织帮助信息。
而且,很明显,在这些用户界面对象中会有一个对象来处理帮助请求;至于是哪一个对象则取
决于上下文以及可用的帮助具体到何种程度。
这儿的问题是提交帮助请求的对象(如按钮)并不明确知道谁是最终提供帮助的对象。我们
要有一种办法将提交帮助请求的对象与可能提供帮助信息的对象解耦( d e c o u p l e )。Chain of
R e s p o n s i b i l i t y模式告诉我们应该怎么做。
这一模式的想法是,给多个对象处理一个请求的机会,从而解耦发送者和接受者。该请
求沿对象链传递直至其中一个对象处理它,如下图所示。
从第一个对象开始,链中收到请求的对象要么亲自处理它,要么转发给链中的下一个候
选者。提交请求的对象并不明确地知道哪一个对象将会处理它-我们说该请求有一个隐式的
接收者(implicit receiver)。
假设用户在一个标有“P r i n t” 的按钮窗口组件上单击帮助,而该按钮包含在一个
P r i n t D i a l o g的实例中,该实例知道它所属的应用对象(见前面的对象框图)。下面的交互框图
(diagram) 说明了帮助请求怎样沿链传递:
在这个例子中,既不是aPrintButton 也不是aPrintDialog 处理该请求;它一直被传递给
a n A p p l i c a t i o n,anApplication 处理它或忽略它。提交请求的客户不直接引用最终响应它的对
象。
要沿链转发请求,并保证接收者为隐式的( i m p l i c i t ),每个在链上的对象都有一致的处理请
求和访问链上后继者的接口。例如,帮助系统可定义一个带有相应的HandleHelp 操作的
H e l p H a n d l e r类。HelpHandler 可为所有候选对象类的父类,或者它可被定义为一个混入
(m i x i n)类。这样想处理帮助请求的类就可将HelpHandler 作为其一个父类,如下页上图所示。
按钮、对话框,和应用类都使用HelpHandler 操作来处理帮助请求。H e l p H a n d l e r的
HandleHelp 操作缺省的是将请求转发给后继。子类可重定义这一操作以在适当的情况下提供
帮助;否则它们可使用缺省实现转发该请求。
3. 适用性
在以下条件下使用Responsibility 链:
• 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
• 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
• 可处理一个请求的对象集合应被动态指定。
4. 结构
一个典型的对象结构可能如下图所示:
5. 参与者
• H a n d l e r(如H e l p H a n d l e r)
- 定义一个处理请求的接口。
- (可选) 实现后继链。
• C o n c r e t e H a n d l e r(如P r i n t B u t t o n和P r i n t D i a l o g)
- 处理它所负责的请求。
- 可访问它的后继者。
- 如果可处理该请求,就处理之;否则将该请求转发给它的后继者。
• C l i e n t
- 向链上的具体处理者( C o n c r e t e H a n d l e r )对象提交请求。
6. 协作
• 当客户提交一个请求时,请求沿链传递直至有一个ConcreteHandler 对象负责处理它。
7. 效果
Responsibility 链有下列优点和缺点( l i a b i l i t i e s ) :
1 ) 降低耦合度该模式使得一个对象无需知道是其他哪一个对象处理其请求。对象仅需
知道该请求会被“正确”地处理。接收者和发送者都没有对方的明确的信息,且链中的对象
不需知道链的结构。
结果是,职责链可简化对象的相互连接。它们仅需保持一个指向其后继者的引用,而不
需保持它所有的候选接受者的引用。
2) 增强了给对象指派职责( R e s p o n s i b i l i t y )的灵活性当在对象中分派职责时,职责链给你
更多的灵活性。你可以通过在运行时刻对该链进行动态的增加或修改来增加或改变处理一个
请求的那些职责。你可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。
3) 不保证被接受既然一个请求没有明确的接收者,那么就不能保证它一定会被处理-
该请求可能一直到链的末端都得不到处理。一个请求也可能因该链没有被正确配置而得不到
处理。
8. 实现
下面是在职责链模式中要考虑的实现问题:
1) 实现后继者链有两种方法可以实现后继者链。
a) 定义新的链接(通常在H a n d l e r中定义,但也可由ConcreteHandlers 来定义)。
b) 使用已有的链接。
我们的例子中定义了新的链接,但你常常可使用已有的对象引用来形成后继者链。例如,
在一个部分-整体层次结构中,父构件引用可定义一个部件的后继者。窗口组件( Wi d g e t)
结构可能早已有这样的链接。C o m p o s i t e(4 . 3)更详细地讨论了父构件引用。
当已有的链接能够支持你所需的链时,完全可以使用它们。这样你不需要明确定义链接,
而且可以节省空间。但如果该结构不能反映应用所需的职责链,那么你必须定义额外的链接。
2) 连接后继者如果没有已有的引用可定义一个链,那么你必须自己引入它们。这种情
况下H a n d l e r 不仅定义该请求的接口,通常也维护后继链接。这样H a n d l e r 就提供了
H a n d l e R e q u e s t 的缺省实现: H a n d l e R e q u e s t 向后继者( 如果有的话) 转发请求。如果
ConcreteHandler 子类对该请求不感兴趣,它不需重定义转发操作,因为它的缺省实现进行无
条件的转发。
此处为一个H e l p H a n d l e r基类,它维护一个后继者链接:
3) 表示请求可以有不同的方法表示请求。最简单的形式,比如在H a n d l e H e l p的例子中,
请求是一个硬编码的(hard-coded) 操作调用。这种形式方便而且安全,但你只能转发H a n d l e r
类定义的固定的一组请求。
另一选择是使用一个处理函数,这个函数以一个请求码(如一个整型常数或一个字符串)为
参数。这种方法支持请求数目不限。唯一的要求是发送方和接受方在请求如何编码问题上应
达成一致。
这种方法更为灵活,但它需要用条件语句来区分请求代码以分派请求。另外,无法用类
型安全的方法来传递请求参数,因此它们必须被手工打包和解包。显然,相对于直接调用一
个操作来说它不太安全。
为解决参数传递问题,我们可使用独立的请求对象来封装请求参数。R e q u e s t类可明确地
描述请求,而新类型的请求可用它的子类来定义。这些子类可定义不同的请求参数。处理者
必须知道请求的类型(即它们正使用哪一个R e q u e s t子类)以访问这些参数。
为标识请求,R e q u e s t可定义一个访问器( a c c e s s o r )函数以返回该类的标识符。或者,如果
实现语言支持的话,接受者可使用运行时的类型信息。
以下为一个分派函数的框架( s k e t c h ),它使用请求对象标识请求。定义于基类R e q u e s t中的
G e t K i n d操作识别请求的类型:
子类可通过重定义H a n d l e R e q u e s t扩展该分派函数。子类只处理它感兴趣的请求;其他的
请求被转发给父类。这样就有效的扩展了(而不是重写) H a n d l e R e q u e s t操作。例如,一个
E x t e n d e d H a n d l e r子类扩展了M y H a n d l e r版本的H a n d l e R e q u e s t :
4) 在S m a l l t a l k中自动转发你可以使用Smalltalk 中的d o e s N o t U n d e r s t a n d机制转发请求。
没有相应方法的消息被doseNotUnderstand 的实现捕捉(trap in),此实现可被重定义,从而可
向一个对象的后继者转发该消息。这样就不需要手工实现转发;类仅处理它感兴趣的请求,
而依赖doesNotUnderstand 转发所有其他的请求。
9. 代码示例
下面的例子举例说明了在一个像前面描述的在线帮助系统中,职责链是如何处理请求的。
帮助请求是一个显式的操作。我们将使用在窗口组件层次中的已有的父构件引用来在链中的
窗口组件间传递请求,并且我们将在H a n d l e r类中定义一个引用以在链中的非窗口组件间传递
帮助请求。
HelpHandler 类定义了处理帮助请求的接口。它维护一个帮助主题(缺省值为空),并保持
对帮助处理对象链中它的后继者的引用。关键的操作是H a n d l e H e l p,它可被子类重定义。
HasHelp 是一个辅助操作,用于检查是否有一个相关的帮助主题。
所有的窗口组件都是Wi d g e t抽象类的子类。Wi d g e t是HelpHandler 的子类,因为所有的用
户界面元素都可有相关的帮助。(我们也可以使用另一种基于混入类的实现方式)
在我们的例子中,按钮是链上的第一个处理者。B u t t o n类是Wi d g e t类的子类。Button 构
造函数有两个参数: 对包含它的窗口组件的引用和其自身的帮助主题。
B u t t o n版本的H a n d l e H e l p首先测试检查其自身是否有帮助主题。如果开发者没有定义一个
帮助主题,就用H e l p H a n d l e r中的H a n d l e H e l p操作将该请求转发给它的后继者。如果有帮助主
题,那么就显示它,并且搜索结束。
D i a l o g实现了一个类似的策略,只不过它的后继者不是一个窗口组件而是任意的帮助请求
处理对象。在我们的应用中这个后继者将是A p p l i c a t i o n的一个实例。
在链的末端是A p p l i c a t i o n的一个实例。该应用不是一个窗口组件,因此A p p l i c a t i o n不是
H e l p H a n d l e r的直接子类。当一个帮助请求传递到这一层时,该应用可提供关于该应用的一般
性的信息,或者它可以提供一系列不同的帮助主题。
下面的代码创建并连接这些对象。此处的对话框涉及打印,因此这些对象被赋给与打印
相关的主题。
我们可对链上的任意对象调用H a n d l e H e l p以触发相应的帮助请求。要从按钮对象开始搜
索,只需对它调用H a n d l e H e l p :
b u t t o n - > H a n d l e H e l p ( ) ;
在这种情况下,按钮会立即处理该请求。注意任何H e l p H a n d l e r类都可作为D i a l o g的后继
者。此外,它的后继者可以被动态地改变。因此不管对话框被用在何处,你都可以得到它正
确的与上下文相关的帮助信息。
10. 已知应用
许多类库使用职责链模式处理用户事件。对H a n d l e r类它们使用不同的名字,但思想是一样
的:当用户点击鼠标或按键盘,一个事件产生并沿链传播。MacApp[App89] 和E T + + [ W G M 8 8 ]
称之为“事件处理者”,S y m a n t e c的T C L库[ S y m 9 3 b ]称之为“B u r e a u c r a t”,而N e X T的A p p K i t命
名为“R e s p o n d e r”。
图形编辑器框架U n i d r a w定义了“命令” C o m m a n d对象,它封装了发给C o m p o n e n t和
C o m p o n e n t Vi e w对象[ V L 9 0 ]的请求。一个构件或构件视图可解释一个命令以进行一个操作,
这里“命令”就是请求。这对应于在实现一节中描述的“对象作为请求” 的方法。构件和构
件视图可以组织为层次式的结构。一个构件或构件视图可将命令解释转发给它的父构件,而
父构件依次可将它转发给它的父构件,如此类推,就形成了一个职责链。
E T + +使用职责链来处理图形的更新。当一个图形对象必须更新它的外观的一部分时,调
用I n v a l i d a t e R e c t操作。一个图形对象自己不能处理I n v a l i d a t e R e c t,因为它对它的上下文了解
不够。例如,一个图形对象可被包装在一些类似滚动条( S c r o l l e r s )或放大器( Z o o m e r s )的对象中,
这些对象变换它的坐标系统。那就是说,对象可被滚动或放大以至它有一部分在视区外。因
此缺省的I n v a l i d a t e R e c t的实现转发请求给包装的容器对象。转发链中的最后一个对象是一个
窗口( Wi n d o w )实例。当窗口收到请求时,保证失效矩形被正确变换。窗口通知窗口系统接口
并请求更新,从而处理I n v a l i d a t e R e c t。
11. 相关模式
职责链常与C o m p o s i t e(4 . 3)一起使用。这种情况下,一个构件的父构件可作为它的后继。
5.2 COMMAND(命令)-对象行为型模式
1. 意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队
或记录请求日志,以及支持可撤消的操作。
2. 别名
动作( A c t i o n ),事务( Tr a n s a c t i o n )
3. 动机
有时必须向某对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息。
例如

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 设计模式是一种通过提供可复用的解决方案来解决面向对象软件开发中常见问题的方法。这些问题可能包括对象之间的通信、对象的创建和销毁、以及如何组织代码等。 设计模式能够提供一种标准化的方法来解决这些问题,使得开发人员可以更加高效地构建软件系统。设计模式提供了一种用于描述和交流解决方案的共同语言,使得不同开发人员之间可以更好地合作。 设计模式的另一个重要特点是其可复用性。一旦开发人员学会了某个设计模式,他们可以在不同的项目中重复使用该模式,从而节省开发时间和资源。这种可复用性使得设计模式成为面向对象软件开发的基础之一。 《设计模式复用面向对象软件基础》这本将深入介绍设计模式的原理、分类和具体实现。通过学习这本,读者可以掌握常见设计模式的应用方法,并了解如何根据具体问题选择最合适的设计模式。该还包含了大量的示例代码和实际案例,帮助读者更好地理解和应用设计模式。 总结来说,设计模式是一种解决面向对象软件开发中常见问题的方法,具有可复用性。《设计模式复用面向对象软件基础》这本通过系统地介绍设计模式的原理、分类和实现,在提高开发效率的同时,帮助读者建立起良好的面向对象软件开发思维方式。 ### 回答2: 设计模式是一种解决软件设计问题的经典方法,它提供了一些通用的解决方案和思想,可用于构建复用面向对象软件设计模式的目标是提高软件的可维护性、可扩展性和灵活性。 设计模式包括三种类型:创建型、结构型和行为型。创建型设计模式关注如何实例化对象,包括简单工厂、工厂方法、抽象工厂、建造者和原型。结构型设计模式关注对象之间的组合,包括适配器、装饰器、代理、组合、外观、享元和桥接。行为型设计模式关注对象之间的通信和职责分配,包括观察者、模板方法、策略、状态、责任链、命令、备忘录、迭代器和访问者。 设计模式可提供可复用的解决方案,不仅可以提高软件的开发效率,还能确保软件的可靠性和可维护性。通过使用设计模式,开发人员可以更加清晰地理解软件系统的结构和功能,使得软件系统更易于理解和维护。同时,设计模式还能促进团队之间的协作和交流,提高团队的开发效率。 《设计模式复用面向对象软件基础》这本提供了系统和详细的介绍和讲解了各种设计模式的原理、实现方法以及应用场景。通过阅读这本,读者可以深入理解设计模式的核心概念,学习如何在实际项目中应用设计模式,提高软件的质量和可维护性。这本对于想要深入学习和应用设计模式软件开发人员来说是一本非常有价值的参考资料。 ### 回答3: 设计模式指的是用于解决软件设计中常见问题的经验性解决方案。它们是软件开发人员在解决类似问题时所提炼出来的最佳实践。设计模式的目标是提高软件的可复用性、可扩展性和可维护性。 设计模式是通过将常见的设计问题和对应的解决方案进行抽象和总结而得到的。它们是由经验丰富的软件开发人员们共同提炼和归纳出来的,是他们在实际项目中不断探索和总结出来的经验。 设计模式是可复用的,因为它们提供了一种标准化的解决方案,可以在不同的项目中重复使用。这样可以减少开发人员的工作量,提高开发效率。 同时,设计模式也能够提升软件的可扩展性,使得软件在面对变化时更加容易进行修改和扩展。通过使用设计模式软件的各个组件之间的耦合性得到了降低,使得系统更加灵活和易于维护。 最后,设计模式使得软件更易于维护。因为设计模式遵循了一系列约定和规范,开发人员能够更快地理解和修改代码,从而降低了维护成本。 设计模式复用面向对象软件基础,是因为它们提供了一套可复用的解决方案,能够解决软件设计中常见的问题。通过学习和应用设计模式,可以提高软件的质量和开发效率,同时也可以提升开发人员的设计能力和职业素养。在软件开发领域中,设计模式是不可或缺的一部分。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coolstar

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值