1. 概述
职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
这里发出这个请求的客户端并不知道这当中的哪一个对象最终处理这个请求,这样系统更改可以在不影响客户端的情况下动态地重新组织和分配责任。
2. 结构图
下面是职责链模式(Chain of Responsibility)的结构图:
- Handler:定义一个处理请求的接口;实现后继链(可选,这是一种后继者的实现方式,上面的结构图就是采用该方式的)。
- ConcreteHandler:处理它所负责的请求;可访问它的后继者;如果可处理该请求,就处理之;否则将该请求转发给它的后继者。
- Client:向链上的具体处理者(ConcreteHandler)对象提交请求。
当客户提交一个请求时,请求沿链传递直至有一个ConcreteHandler对象负责处理它。
这里要说下的是在职责链模式中有两种方法可以实现后继者链:
- 定义新的链接(通常在Handler中定义,但也可由ConcreteHandler来定义)。
- 使用已有的链接。
3. 实例分析
考虑一个图形用户界面中的上下文有关的帮助机制。用户在界面的任一部分上点击就可以得到帮助信息,所提供的帮助依赖于点击的是界面的哪一部分以及其上下文。例如,对话框中的按钮的帮助信息就可能和主窗口中类似的按钮不同,如果对那一部分界面没有特定的帮助信息,那么帮助系统应该显示一个关于当前上下文的较一般的帮助信息,比如说,整个对话框。
因此很自然地,应根据普遍性即从特殊到最普通的顺序来组织帮助信息。而且,很明显,在这些用户界面对象中会有一个对象来处理帮助请求;至于是哪一个对象则取决于上下文以及可用到的帮助具体到何种程度。
这儿的问题是提交帮助请求的对象(如按钮)并不明确知道谁是最终提供帮助的对象。我们要有一种办法将提交帮助请求的对象与可能提供帮助信息的对象解耦。职责链模式告诉我们怎么处理这样的问题。
这一模式的想法是,给多个对象处理一个请求的机会,从而解耦发送者和接收者,该请求沿着对象链传递直至其中一个对象处理它。
/*********************************************************************************
*Copyright(C),Your Company
*FileName: HelpInfo.h
*Author: Huangjh
*Version:
*Date: 2017-11-23
*Description: 相当于结构图中的Handler角色——定义处理帮助请求的接口
*Others:
**********************************************************************************/
#pragma once
#ifndef _HELP_INFO_H
#define _HELP_INFO_H
#include <cstddef>
#include <string>
class CHelpInfo
{
public:
CHelpInfo()
:m_pConcreteHandler(NULL)
{
}
virtual ~CHelpInfo() { }
void setSuccessor(CHelpInfo *pConcreteHandler)
{
m_pConcreteHandler = pConcreteHandler;
}
virtual void HandlerRequest(std::string &strRequet) = 0;
protected:
CHelpInfo *m_pConcreteHandler;
};
#endif //#ifndef _HELP_INFO_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: DialogHelpInfo.h
*Author: Huangjh
*Version:
*Date: 2017-11-23
*Description: 相当于结构图中的ConcreteHandler角色——定义对话框中按钮的帮助信息
*Others:
**********************************************************************************/
#pragma once
#ifndef _DIALOG_HELP_INOF_H
#define _DIALOG_HELP_INOF_H
#include <iostream>
#include "HelpInfo.h"
class CDialogHelpInfo : public CHelpInfo
{
public:
void HandlerRequest(std::string &strRequest)
{
if (strRequest == "对话框")
{
std::cout << "对话框中的按钮帮助信息" << std::endl;
}
else
{
std::cout << "请求的不是对话框的帮助信息,向职责链中传递" << std::endl;
if (m_pConcreteHandler != NULL)
m_pConcreteHandler->HandlerRequest(strRequest);
}
}
};
#endif //#ifndef _DIALOG_HELP_INOF_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: MenuHelpInfo.h
*Author: Huangjh
*Version:
*Date: 2017-11-23
*Description: 相当于结构图中的ConcreteHandler角色——定义主菜单中按钮的帮助信息
*Others:
**********************************************************************************/
#pragma once
#ifndef _MENU_HELP_INOF_H
#define _MENU_HELP_INOF_H
#include <iostream>
#include "HelpInfo.h"
class CMenuHelpInfo : public CHelpInfo
{
public:
void HandlerRequest(std::string &strRequest)
{
if (strRequest == "主菜单")
{
std::cout << "主菜单中的按钮帮助信息" << std::endl;
}
else
{
std::cout << "请求的不是主菜单的帮助信息,向职责链中传递" << std::endl;
if (m_pConcreteHandler != NULL)
m_pConcreteHandler->HandlerRequest(strRequest);
}
}
};
#endif //#ifndef _MENU_HELP_INOF_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: TextHelpInfo.h
*Author: Huangjh
*Version:
*Date: 2017-11-23
*Description: 相当于结构图中的ConcreteHandler角色——定义文本框中按钮的帮助信息
*Others:
**********************************************************************************/
#pragma once
#ifndef _TEXT_HELP_INOF_H
#define _TEXT_HELP_INOF_H
#include <iostream>
#include "HelpInfo.h"
class CTextHelpInfo : public CHelpInfo
{
public:
void HandlerRequest(std::string &strRequest)
{
if (strRequest == "文本")
{
std::cout << "文本框中的按钮帮助信息" << std::endl;
}
else
{
std::cout << "请求的不是文本框的帮助信息,向职责链中传递" << std::endl;
if (m_pConcreteHandler != NULL)
m_pConcreteHandler->HandlerRequest(strRequest);
}
}
};
#endif //#ifndef _TEXT_HELP_INOF_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: main.cpp
*Author: Huangjh
*Version:
*Date: 2017-11-23
*Description: 相当于结构图中的Client角色——职责链模式的测试用例
*Others:
**********************************************************************************/
#include "HelpInfo.h"
#include "DialogHelpInfo.h"
#include "MenuHelpInfo.h"
#include "TextHelpInfo.h"
int main(void)
{
std::string strHelpRequest("文本");
CHelpInfo *pDialogHelp = new CDialogHelpInfo();
CHelpInfo *pMenuHelp = new CMenuHelpInfo();
CHelpInfo *pTextHelp = new CTextHelpInfo();
pDialogHelp->setSuccessor(pMenuHelp);
pMenuHelp->setSuccessor(pTextHelp);
pDialogHelp->HandlerRequest(strHelpRequest);
return 0;
}
运行结果如下所示:
请求的不是对话框的帮助信息,向职责链中传递
请求的不是主菜单的帮助信息,向职责链中传递
文本框中的按钮帮助信息
4. 优缺点
4.1 优点
- 降低耦合度 该模式使得一个对象无需知道是其他哪一个对象处理其请求。对象仅需知道该请求会被“正确”地处理。接收者和发送者都没有对方的明确的信息,且链中的对象不需要知道链的结构。结果是,职责链可以简化对象的相互连接。它们仅需要保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。
- 增加了给对象指派职责的灵活性 当在对象中分派职责时,职责链给你更多的灵活性。你可以通过在运行时刻对该链进行动态的增加或修改来增加或改变处理一个请求的那些职责。你可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。
4.2 缺点
- 不保证被接受 既然一个请求没有明确的接收者,那么就不能保证它一定会被处理——该请求可能一直到链的末端都得不到处理。一个请求也可能因为该链没有被正确配置而得不到处理。
5. 适用性
在以下条件下使用职责链:
- 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确认。
- 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可处理一个请求的对象集合应被动态指定。
6. 参考资料
- 《大话设计模式》
- 《设计模式——可复用的面向对象的基础》