责任链模式

<image>

在物件导向程式设计里，责任链模式（chain-of-responsibility pattern），是一种行为型软件设计模式，它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象，它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。还存在往该处理链的末尾添加新的处理对象的机制。

## 结构

<image>

在上面的UML类图中，`Sender`类不直接提及到特定的接收者类。`Sender`转而提及到了`Handler`接口，通过它来处理一个请求（`handler.handleRequest()`），这使得`Sender`独立于处理这个请求的接收者。`Receiver1`、`Receiver2`和`Receiver3`类实现了`Handler`接口，它们要么处理要么转发一个请求（依赖于运行时条件）。

UML序列图展示了运行时交互：在这个例子中，`Sender`对象调用`handleRequest()`于`receiver1`对象之上（它具有类型`Handler`）。`receiver1`转发这个请求至`receiver2`，它接着转发这个请求至`receiver3`，它处理了这个请求。

## 代码示例

### C++

《设计模式》书中早于C++98的例子的C++23实现：

```c++
import std;

using std::shared_ptr;

enum class Topic: char {
    NO_HELP_TOPIC = 0,
    PRINT = 1,
    PAPER_ORIENTATION = 2,
    APPLICATION = 3,
    // more topics
};

// Abstract handler
// defines an interface for handling requests.
class HelpHandler {
private:
    HelpHandler* successor;
    Topic topic;
public:
    explicit HelpHandler(HelpHandler* h = nullptr, Topic t = Topic::NO_HELP_TOPIC): 
        successor{h}, topic{t} {}

    [[nodiscard]]
    virtual bool hasHelp() const noexcept {
        return topic != Topic::NO_HELP_TOPIC;
    }

    virtual void setHandler(HelpHandler*, Topic) = 0;

    virtual void handleHelp() const {
        std::println("HelpHandler::handleHelp called");
        // (optional) implements the successor link.
        if (!successor) {
            successor->handleHelp();
        }
    }

    virtual ~HelpHandler() = default;

    HelpHandler(const HelpHandler&) = delete;
    HelpHandler& operator=(const HelpHandler&) = delete;
};

class Widget : public HelpHandler {
private:
    Widget* parent;
protected:
    explicit Widget(Widget* w, Topic t = Topic::NO_HELP_TOPIC): 
        HelpHandler(w, t), parent{w} {
        parent = w;
    }
public:
    Widget(const Widget&) = delete;
    Widget& operator=(const Widget&) = delete;
};

// Concrete handler
// handles requests it is responsible for.
class Button : public Widget {
public:
    explicit Button(shared_ptr<Widget> h, Topic t = NO_HELP_TOPIC): 
        Widget(h.get(), t) {}

    virtual void handleHelp() const {
        // if the Concrete handler can handle the request, it does so; otherwise it forwards the request to its successor.
        std::println("Button::handleHelp called");
        if (hasHelp()) {
            // handles requests it is responsible for.
        } else {      
            // can access its successor.
            HelpHandler::handleHelp();
        }
    }
};

// Concrete handler
class Dialog : public Widget {
public:
    explicit Dialog(shared_ptr<HelpHandler> h, Topic t = Topic::NO_HELP_TOPIC): 
        Widget(nullptr) {
        setHandler(h.get(), t);
    }

    virtual void handleHelp()const  {
        std::println("Dialog::handleHelp called");
        // Widget operations that Dialog overrides...
        if (hasHelp()) {
            // offer help on the dialog
        } else {
            HelpHandler::handleHelp();
        }
    }
};

class Application : public HelpHandler {
public:
    explicit Application(Topic t): 
        HelpHandler(nullptr, t) {}

    virtual void handleHelp() const {
        std::println("Application::handleHelp called");
        // show a list of help topics
    }
};

int main(int argc, char* argv[]) {
    shared_ptr<Application> application = std::make_shared<Application>(Topic::APPLICATION_TOPIC);
    shared_ptr<Dialog> dialog = std::make_shared<Dialog>(application, Topic::PRINT_TOPIC);
    shared_ptr<Button> button = std::make_shared<Button>(dialog, Topic::PAPER_ORIENTATION_TOPIC);

    button->handleHelp();
    return 0;
}
```

### Python

下面是Python的例子：

```python
from abc import ABC, abstractmethod

class Handler(ABC):
    def __init__(self, successor=None):
        self.successor = successor
    @abstractmethod
    def __call__(self): pass

topic_list = [
    'Application topic', 'Print topic',
    'Paper orientation topic']

class HelpHandler(Handler):
    def __init__(self, topic, *args):
        assert topic in topic_list
        self.topic = topic
        super().__init__(*args)
    def __call__(self, topic):
        if self.topic == topic:
            print(f"Help with {topic}") 
        elif self.successor is None:
            print(f"No help with {topic}")
        else:
            self.successor(topic)

class Application():
    def __init__(self, handler=None):
        self.handler = handler
    def handle_help(self, topic):
        if self.handler is not None:
            self.handler(topic)

class Widget():
    def __init__(self, parent, handler=None):
        self.parent = parent
        self.handler = handler
    def handle_help(self, topic):
        if self.handler is not None:
            self.handler(topic)
    def draw(self): pass

class Dialog(Widget): pass

class Button(Widget): pass

def demo():
    chain = HelpHandler(topic_list[0])
    application = Application(chain)
    chain = HelpHandler(topic_list[1], chain)
    dialog = Dialog(None, chain)
    chain = HelpHandler(topic_list[2], chain)
    button = Button(dialog, chain)
    button.handle_help(topic_list[2])
    button.handle_help(topic_list[1])
    button.handle_help(topic_list[0])
    button.handle_help('No such topic')
```

其执行：

```pycon
>>> demo()
Help with Paper orientation topic
Help with Print topic
Help with Application topic
No help with No such topic
```

### Java

下面是Java的日志类(logging)例子演示了该模式。每一个logging handler首先决定是否需要在该层做处理，然后将控制传递到下一个logging handler。程序的输出是：

```output
   Writting to stdout: Entering function y.
   Writting to stdout: Step1 completed.
   Sending via e-mail:      Step1 completed.
   Writting to stdout: An error has occurred.
   Sending via e-mail:      An error has occurred.
   Writing to stderr:       An error has occurred.
```

注意：该例子不是日志类的推荐实现方式。

同时，需要注意的是，通常在责任链模式的实现中，如果在某一层已经处理了这个logger，那么这个logger就不会传递下去。在我们这个例子中，消息会一直传递到最底层不管它是否已经被处理。

```java
import java.util.*;

abstract class Logger {
    public static int ERR = 3;
    public static int NOTICE = 5;
    public static int DEBUG = 7;
    protected int mask;

    // The next element in the chain of responsibility
    protected Logger next;
    public Logger setNext(Logger l) {
        next = l;
        return this;
    }

    public final void message( String msg, int priority) {
        if (priority <= mask) {
            writeMessage(msg);
            if (next != null) {
                next.message(msg, priority);
            }
        }
    }
    
    protected abstract void writeMessage(String msg);
}

class StdoutLogger extends Logger {
    public StdoutLogger(int mask) { this.mask = mask; }

    protected void writeMessage(String msg) {
        System.out.println("Writting to stdout: " + msg);
    }
}

class EmailLogger extends Logger {
    public EmailLogger(int mask) { this.mask = mask; }

    protected void writeMessage(String msg) {
        System.out.println("Sending via email: " + msg);
    }
}

class StderrLogger extends Logger {
    public StderrLogger(int mask) { this.mask = mask; }

    protected void writeMessage(String msg) {
        System.out.println("Sending to stderr: " + msg);
    }
}

public class ChainOfResponsibilityExample {
    public static void main(String[] args) {
        // Build the chain of responsibility
        Logger l = new StdoutLogger(Logger.DEBUG).setNext(
                       new EmailLogger(Logger.NOTICE).setNext(
                           new StderrLogger(Logger.ERR)));

        // Handled by StdoutLogger
        l.message("Entering function y.", Logger.DEBUG);

        // Handled by StdoutLogger and EmailLogger
        l.message("Step1 completed.", Logger.NOTICE);

        // Handled by all three loggers
        l.message("An error has occurred.", Logger.ERR);
    }
}
```

## 外部链接

- Article " The Chain of Responsibility pattern's pitfalls and improvements" by Michael Xinsheng Huang
- Article " Follow the Chain of Responsibility" by David Geary
- Article " Pattern Summaries: Chain of Responsibility" by Mark Grand
- CoR overview
- Behavioral Patterns - Chain of Responsibility Pattern
- Descriptions from Portland Pattern Repository
- Apache Jakarta Commons Chain