[设计模式] 设计模式课程(十九)--职责链模式

概述

  • 属于行为型
  • 程序需要按顺序处理多个请求时
  • 程序需要使用不同方式处理不同种类的请求,且请求类型和顺序预先未知时,将多个处理者连成一条链,接到请求后,询问每个处理者是否能对其进行处理
  • 沿责任链传递请求,直到有一个对象处理为止。使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系

  • 一个请求可能会被多个对象处理,但每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求者与接受者的紧耦合
  • 如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使二者解耦
  • 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止
  • 使用多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,知道有一个对象处理它为止
  • Chain of Responsibility 模式的应用场合在于“一个请求可能有多个接受者,但最后真正的接受者只有一个”,这时候请求发送者与接受者的耦合有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化
  • 应用了Chain of Responsibility 模式后,对象的职责分派将更具灵活性,可在运行时动态添加/修改请求的处理职责
  • 如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制,这也是每一个接收对象的职责,而不是发出请求的对象的责任

场景

  • 拨打技术支持电话时,多名接听人员接力处理
  • 为活动的GUI元素显示上下文帮助信息。当用户鼠标移动到某个元素并按下F1键时,程序检测到指针下的组件并对其发送帮助请求,该请求不断向上传递到该元素的所有容器,直到某个元素能显示帮助信息
  • Struct2的拦截器
  • jsp servlet 的 Filter
  • Tomca 对 Encoding 的处理

结构

  • 处理者:声明了所有具体处理者的通用接口
  • 基础处理者:放置所有处理者共用的代码
  • 具体处理者:处理请求的实际代码,处理者接到具体请求后,决定是否处理,是否沿着链传递请求
  • 客户端:根据程序逻辑一次性或动态地生成链,请求可以发给链上的任一个处理者

 示例1

ChainofResponsibility.cpp

View Code
  • 举例:windows界面
  • 25行:用链表实现链,基类的多态指针指向自身,形成多态链表
  • 38-45行:整体处理

示例2

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 using namespace std;
 5 
 6 class Request{
 7     public:
 8         string m_strContent;
 9         int m_nNumber;
10 };
11 class Manager{
12     protected:
13         Manager* manager;
14         string name;
15     public:
16         Manager(string temp){
17             name = temp;
18         }
19     void SetSuccessor(Manager* temp){
20         manager = temp;
21     } 
22     virtual void GetRequest(Request* request)=0;
23 };
24 class CommonManager:public Manager{
25     public:
26         CommonManager(string strTemp):Manager(strTemp){}
27         virtual void GetRequest(Request* request){
28             if(request->m_nNumber>=0 && request->m_nNumber<10){
29                 cout<<name<<"处理了"<<request->m_nNumber<<"个请求"<<endl;
30             }else{
31                 manager->GetRequest(request);
32             }
33         }
34 };
35 class MajorDomo:public Manager{
36     public:
37         MajorDomo(string name):Manager(name){}
38         virtual void GetRequest(Request* request){
39             if(request->m_nNumber>=10){
40                 cout<<name<<"处理了"<<request->m_nNumber<<"个请求"<<endl;
41             }
42         }
43 };
44 int main(){
45     Manager* common = new CommonManager("张经理");
46     Manager* major = new MajorDomo("李总监");
47     common->SetSuccessor(major);
48     Request* req = new Request();
49     
50     req->m_nNumber = 33;
51     common->GetRequest(req);
52     
53     req->m_nNumber=3;
54     common->GetRequest(req);
55     
56     return 0;
57 }

示例3

 1 // 处理者接口声明了一个创建处理者链的方法。还声明了一个执行请求的方法。
 2 interface ComponentWithContextualHelp is
 3     method showHelp()
 4 
 5 
 6 // 简单组件的基础类。
 7 abstract class Component implements ComponentWithContextualHelp is
 8     field tooltipText: string
 9 
10     // 组件容器在处理者链中作为“下一个”链接。
11     protected field container: Container
12 
13     // 如果组件设定了帮助文字,那它将会显示提示信息。如果组件没有帮助文字
14     // 且其容器存在,那它会将调用传递给容器。
15     method showHelp() is
16         if (tooltipText != null)
17             // 显示提示信息。
18         else
19             container.showHelp()
20 
21 
22 // 容器可以将简单组件和其他容器作为其子项目。链关系将在这里建立。该类将从
23 // 其父类处继承 showHelp(显示帮助)的行为。
24 abstract class Container extends Component is
25     protected field children: array of Component
26 
27     method add(child) is
28         children.add(child)
29         child.container = this
30 
31 
32 // 原始组件应该能够使用帮助操作的默认实现...
33 class Button extends Component is
34     // ...
35 
36 // 但复杂组件可能会对默认实现进行重写。如果无法以新的方式来提供帮助文字,
37 // 那组件总是还能调用基础实现的(参见 Component 类)。
38 class Panel extends Container is
39     field modalHelpText: string
40 
41     method showHelp() is
42         if (modalHelpText != null)
43             // 显示包含帮助文字的模态窗口。
44         else
45             super.showHelp()
46 
47 // ...同上...
48 class Dialog extends Container is
49     field wikiPageURL: string
50 
51     method showHelp() is
52         if (wikiPageURL != null)
53             // 打开百科帮助页面。
54         else
55             super.showHelp()
56 
57 
58 // 客户端代码。
59 class Application is
60     // 每个程序都能以不同方式对链进行配置。
61     method createUI() is
62         dialog = new Dialog("预算报告")
63         dialog.wikiPageURL = "http://..."
64         panel = new Panel(0, 0, 400, 800)
65         panel.modalHelpText = "本面板用于..."
66         ok = new Button(250, 760, 50, 20, "确认")
67         ok.tooltipText = "这是一个确认按钮..."
68         cancel = new Button(320, 760, 50, 20, "取消")
69         // ...
70         panel.add(ok)
71         panel.add(cancel)
72         dialog.add(panel)
73 
74     // 想象这里会发生什么。
75     method onF1KeyPress() is
76         component = this.getComponentAtMouseCoords()
77         component.showHelp()
View Code

示例4

  1 #include <iostream>
  2 #include <string> 
  3 #include <vector>
  4 /**
  5  * The Handler interface declares a method for building the chain of handlers.
  6  * It also declares a method for executing a request.
  7  */
  8 class Handler {
  9  public:
 10   virtual Handler *SetNext(Handler *handler) = 0;
 11   virtual std::string Handle(std::string request) = 0;
 12 };
 13 /**
 14  * The default chaining behavior can be implemented inside a base handler class.
 15  */
 16 class AbstractHandler : public Handler {
 17   /**
 18    * @var Handler
 19    */
 20  private:
 21   Handler *next_handler_;
 22 
 23  public:
 24   AbstractHandler() : next_handler_(nullptr) {
 25   }
 26   Handler *SetNext(Handler *handler) override {
 27     this->next_handler_ = handler;
 28     // Returning a handler from here will let us link handlers in a convenient
 29     // way like this:
 30     // $monkey->setNext($squirrel)->setNext($dog);
 31     return handler;
 32   }
 33   std::string Handle(std::string request) override {
 34     if (this->next_handler_) {
 35       return this->next_handler_->Handle(request);
 36     }
 37 
 38     return {};
 39   }
 40 };
 41 /**
 42  * All Concrete Handlers either handle a request or pass it to the next handler
 43  * in the chain.
 44  */
 45 class MonkeyHandler : public AbstractHandler {
 46  public:
 47   std::string Handle(std::string request) override {
 48     if (request == "Banana") {
 49       return "Monkey: I'll eat the " + request + ".
";
 50     } else {
 51       return AbstractHandler::Handle(request);
 52     }
 53   }
 54 };
 55 class SquirrelHandler : public AbstractHandler {
 56  public:
 57   std::string Handle(std::string request) override {
 58     if (request == "Nut") {
 59       return "Squirrel: I'll eat the " + request + ".
";
 60     } else {
 61       return AbstractHandler::Handle(request);
 62     }
 63   }
 64 };
 65 class DogHandler : public AbstractHandler {
 66  public:
 67   std::string Handle(std::string request) override {
 68     if (request == "MeatBall") {
 69       return "Dog: I'll eat the " + request + ".
";
 70     } else {
 71       return AbstractHandler::Handle(request);
 72     }
 73   }
 74 };
 75 /**
 76  * The client code is usually suited to work with a single handler. In most
 77  * cases, it is not even aware that the handler is part of a chain.
 78  */
 79 void ClientCode(Handler &handler) {
 80   std::vector<std::string> food = {"Nut", "Banana", "Cup of coffee"};
 81   for (const std::string &f : food) {
 82     std::cout << "Client: Who wants a " << f << "?
";
 83     const std::string result = handler.Handle(f);
 84     if (!result.empty()) {
 85       std::cout << "  " << result;
 86     } else {
 87       std::cout << "  " << f << " was left untouched.
";
 88     }
 89   }
 90 }
 91 /**
 92  * The other part of the client code constructs the actual chain.
 93  */
 94 int main() {
 95   MonkeyHandler *monkey = new MonkeyHandler;
 96   SquirrelHandler *squirrel = new SquirrelHandler;
 97   DogHandler *dog = new DogHandler;
 98   monkey->SetNext(squirrel)->SetNext(dog);
 99 
100   /**
101    * The client should be able to send a request to any handler, not just the
102    * first one in the chain.
103    */
104   std::cout << "Chain: Monkey > Squirrel > Dog

";
105   ClientCode(*monkey);
106   std::cout << "
";
107   std::cout << "Subchain: Squirrel > Dog

";
108   ClientCode(*squirrel);
109 
110   delete monkey;
111   delete squirrel;
112   delete dog;
113 
114   return 0;
115 }
View Code
Chain: Monkey > Squirrel > Dog

Client: Who wants a Nut?
  Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
  Monkey: I'll eat the Banana.
Client: Who wants a Cup of coffee?
  Cup of coffee was left untouched.

Subchain: Squirrel > Dog

Client: Who wants a Nut?
  Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
  Banana was left untouched.
Client: Who wants a Cup of coffee?
  Cup of coffee was left untouched.

参考

https://refactoringguru.cn/design-patterns/chain-of-responsibility

原文地址:https://www.cnblogs.com/cxc1357/p/12077331.html