问题聚焦:
行为模式,将你的注意力从控制流转移到对象间的联系通信方式上来。
本节所介绍的职责链,是典型的松耦合设计模式。
职责链模式可以相应多个请求接受者,选择其中最合适的处理者。
行为模式:
定义:算法和对象间职责的分配,不仅描述对象或类的模式,还描述它们之间的通信模式。
使用继承机制:使用继承机制在类间分派行为。如Tempalte Method和Interpreter。
使用对象复合:一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任一个对象都无法单独完成的任务。其要注意的问题是如何低耦合又保持相互的了解。如Mediator模式提供的松耦合所需的间接性。
职责链(Chain of Responsibility)通过一条候选对象链式向一个对象发送请求。
观察者模式(Oberserver)定义并保持对象间的依赖关系。
其他的行为对象模式常将行为封装在一个对象中并将请求指派给它。
职责链(Chain Of Responsibility)
意图:
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
动机:
依然以一个实际的例子说明该模式的设计出发点。
Demo:
考虑一个图形用户界面中的帮助机制设计
需求:用户在界面的任一部分上点击就可以得到帮助信息,所提供的帮助依赖于点击的是界面的哪一部分以及其上下文。
如果对那一部分界面没有特定的帮助信息,那么帮助系统应该显示一个关于当前上下文的较一般的帮助信息。比如,整个对话框的帮助信息。
思路:从特殊到普遍的顺序组织帮助信息,有一个对象专门负责选择帮助请求。
挑战:提交帮助请求的对象并不明确知道谁是最终提供帮助的对象。我们要做的是将提交帮助请求的对象与可能提供帮助信息的对象解耦和。
给多个对象处理一个请求的机会,挑选最适合的处理者,从而解耦发送者和接受者。该请求将按对象链传递,知道一个合适的请求接受者对它作出反应。
如下图所示:
在PrintButton和OKButton接收到的请求并不直接处理,而是沿转发链向下转发至PrintDialog,一直到Application,Application处理它或忽略它。
效果就是,提交请求的客户不直接引用最终相应它的对象。
实现:要沿链转发请求,并保证接收者为隐式的(implicit),每个链上的对象都有一致的处理请求和访问链上后继者的接口。
设计:HelpHandler类,作为所有候选对象类的父类,用HelpHandler定义的接口处理帮助请求,或者下发请求。
HandleHelp操作缺省的是将请求转发给后继,子类可重定义这一操作以在适当的情况下提供帮助;否则它们可使用缺省实现转发该请求。
适用性:
以下情况使用Responsibility链:
- 有多个的对象可以处理一个请求,哪个对象处理该请求,在运行时刻自动确定。
- 你想在不明确指定接受者的情况下,向多个对象中的一个,提交一个请求。
- 可处理一个请求的对象集合应被动态指定。
结构:
一个典型的对象结构可能如下图所示:
参与者:
- Handler(HelpHandler):
- Concretehandler(PrintButton):
- 处理它负责的请求
- 可访问它的后继者
- 如果可处理该请求,就处理;否则将该请求转发给它的后继者
- Client:向链上的具体处理者对象提交请求
协作:
当客户提交一个请求时,请求沿链传递直至有一个ConcreteHandler对象负责处理它。
效果:
优点和缺点
- 降低耦合度:请求提交者无需知道哪一个对象处理请求;请求接受者不需要知道链的存在,它只需要知道它的后继是谁。
- 增强了给对象指派职责的灵活度
- 不保证被接受:当请求到达链尾仍没有被处理时,就得不到处理。
实现:
下面是在职责链模式中要考虑的实现问题:
- 实现后继者链(接受请求者组成的处理请求链):
- 定义新的链接
- 使用已有的链接:在组合模式(Composite)中详细地讨论了适合作后继者链的结构
- 连接后继者:如果没有一个可使用的引用定义一个链,那么Handler不仅定义该请求的接口,通常也维护后继链接。这时,Concretehandler子类对该请求不感兴趣时,进行无条件转发。
- 表示请求:三种方式表示请求
- 简单地,硬编码操作调用,缺点,只能转发定义的固定的一组请求
- 使用处理函数,接受一个请求码。
- 使用独立的请求对象请求参数。
代码示例:
下面以前面所述的在线帮助系统,来演示职责链是如何处理请求的。
类设定:
HelpHandler类定义了处理帮助请求的接口。
职责:维护一个帮助主题;保持对帮助处理对象链中的后继者的引用
关键操作:HanldeHelp,可被子类重定义;HasHelp辅助操作,用于检查是否有一个相关的帮助主题。
typedef int Topic;
const Topic NO_HELP_TOPIC = -1;
class HelpHandler {
public:
HelpHanlder(HelpHandler* = 0, Topic = NO_HELP_TOPIC);
virtual bool HasHelp();
virtual void SetHandler(HelpHandler*, Topic);
virtual void HandleHelp();
private:
HelpHandler* _successor;
Topic _topic;
};
HelpHandler::HelpHandler(HelpHandler* h, Topic t)
: _successor(h), _topic(t) {}
HelpHandler::HandlerHelp () {
if (_successor != 0) {
_successor->HandleHelp();
}
}
Widget抽象类:所有窗口组件的父类
class Widget : public HelpHandler {
protected:
Widget(Widget* parent, Topic t = NO_HELP_TOPIC);
private:
Widget* _parent;
};
Widget::Widget (Widget* w, Topic t) : HelpHandler(w, t) {
_parent = w;
}
按钮是链上的第一个处理者
class Button : public Widget {
public:
Button(Widget* d, Topic t = NO_HELP_TOPIC);
virtual void HandleHelp();
};
Button::Button (Widget* h , Topic t) : Widget(h, t) { }
void Button::HandleHelp () {
if (HasHelp()) {
// offer help on the request
}
else {
HelpHandler::HandleHelp();
}
}
Dialog实现了一个类似的策略。其后继者不仅可以是一个窗口组件,还可以是任意的帮助请求处理对象。
class Dialog : public Widget {
public:
Dialog(HelpHandler* h, Topic t = NO_HELP_TOPIC);
virtual void HandleHelp();
};
Dialog::Dialog(HelpHandler* h, Topic t) : Widget(0) {
SetHandler(h, t);
}
void Dialog::HandleHelp() {
if (HasHelp()) {
// offer help
}
else {
HelpHandler::HandleHelp();
}
}
在链的末端是Application的一个实例。
class Application : public HelpHandler {
......
};
类设定完毕。
下面的代码创建并连接这些对象。
const Topic PRINT_TOPIC = 1;
const Topic PAPER_ORIENTATION_TOPIC = 2;
const Topic APPLICATION_TOPIC = 3;
Application* application = new Application(APPLICATION_TOPIC);
Dialog* dialog = new Dialog(application, PRINT_TOPIC);
Button* button = new Button(dialog, PAPER_ORIENTATION_TOPIC);
我们可对链上的任意对象调用HandleHelp以触发相应的帮助请求。
相关模式:
职责链模式常与Composite模式一起使用,这种情况下,一个构件的父构件可以作为它的后继。
参考资料:
《设计模式:可复用面向对象软件的基础》