热门问题
时间线
聊天
视角

責任鏈模式

来自维基百科,自由的百科全书

责任链模式
Remove ads

物件導向程式設計裡,責任鏈模式(英語:chain-of-responsibility pattern),是一種行為型軟件設計模式,它包含了一些命令對象和一系列的處理對象。每一個處理對象決定它能處理哪些命令對象,它也知道如何將它不能處理的命令對象傳遞給該鏈中的下一個處理對象。還存在往該處理鏈的末尾添加新的處理對象的機制。

Thumb
責任鏈的UML類圖

結構

Thumb
責任鏈模式的樣例UML類圖和序列圖[1]

在上面的UML類圖中,Sender類不直接提及到特定的接收者類。Sender轉而提及到了Handler接口,通過它來處理一個請求(handler.handleRequest()),這使得Sender獨立於處理這個請求的接收者。Receiver1Receiver2Receiver3類實現了Handler接口,它們要麼處理要麼轉發一個請求(依賴於運行時條件)。

UML序列圖展示了運行時交互:在這個例子中,Sender對象調用handleRequest()receiver1對象之上(它具有類型Handler)。receiver1轉發這個請求至receiver2,它接着轉發這個請求至receiver3,它處理了這個請求。

代碼示例

C++

設計模式》書中早於C++98的例子的C++11實現[2]

#include <iostream>
#include <memory>

typedef int Topic;
constexpr Topic NO_HELP_TOPIC = -1;

// defines an interface for handling requests.
class HelpHandler { // Handler
public:
    HelpHandler(HelpHandler* h = nullptr, Topic t = NO_HELP_TOPIC)
        : successor(h), topic(t) {}
    virtual bool hasHelp() {
        return topic != NO_HELP_TOPIC;
    }
    virtual void setHandler(HelpHandler*, Topic) {}
    virtual void handleHelp() {
        std::cout << "HelpHandler::handleHelp\n";
        // (optional) implements the successor link.
        if (successor != nullptr) {
            successor->handleHelp();
        }
    }
    virtual ~HelpHandler() = default;
    HelpHandler(const HelpHandler&) = delete; // rule of three
    HelpHandler& operator=(const HelpHandler&) = delete;
private:
    HelpHandler* successor;
    Topic topic;
};

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

// handles requests it is responsible for.
class Button : public Widget { // ConcreteHandler
public:
    Button(std::shared_ptr<Widget> h, Topic t = NO_HELP_TOPIC) : Widget(h.get(), t) {}
    virtual void handleHelp() {
        // if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor.
        std::cout << "Button::handleHelp\n";
        if (hasHelp()) {
            // handles requests it is responsible for.
        } else {      
            // can access its successor.
            HelpHandler::handleHelp();
        }
    }
};

class Dialog : public Widget { // ConcreteHandler
public:
    Dialog(std::shared_ptr<HelpHandler> h, Topic t = NO_HELP_TOPIC) : Widget(nullptr) {
        setHandler(h.get(), t);
    }
    virtual void handleHelp() {
        std::cout << "Dialog::handleHelp\n";
        // Widget operations that Dialog overrides...
        if(hasHelp()) {
            // offer help on the dialog
        } else {
            HelpHandler::handleHelp();
        }
    }
};

class Application : public HelpHandler {
public:
    Application(Topic t) : HelpHandler(nullptr, t) {}
    virtual void handleHelp() {
        std::cout << "Application::handleHelp\n";
        // show a list of help topics
    }
};

int main() {
    constexpr Topic PRINT_TOPIC = 1;
    constexpr Topic PAPER_ORIENTATION_TOPIC = 2;
    constexpr Topic APPLICATION_TOPIC = 3;
    // The smart pointers prevent memory leaks.
    std::shared_ptr<Application> application = std::make_shared<Application>(APPLICATION_TOPIC);
    std::shared_ptr<Dialog> dialog = std::make_shared<Dialog>(application, PRINT_TOPIC);
    std::shared_ptr<Button> button = std::make_shared<Button>(dialog, PAPER_ORIENTATION_TOPIC);

    button->handleHelp();
}
Remove ads

Python

下面是Python的例子:

from abc import ABC, abstractmethod
from weakref import ref

class Handler(ABC):
    @abstractmethod
    def __call__(self): pass
    def set_handler(self, sucessor):
        self.__successor = successor

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

class HelpHandler(Handler):
    def __init__(self, topic, perform, successor=None):
        assert topic in topic_list
        self.__topic = topic
        self.__perform = perform        
        self.__successor = successor
    def __call__(self, topic):
        if self.__topic == topic:
            self.__perform(self, topic)
        elif self.__successor is None:
            print(f"No help with {topic}")
        else:
            self.__successor(topic)

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

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

class Dialog(Widget): pass

class Button(Widget): pass

def demo():
    def func(self, topic):
        print(f"Help with {topic}") 
    handler_chain = HelpHandler(topic_list[0], func)
    application = Application(handler_chain)
    handler_chain = HelpHandler(topic_list[1], func, handler_chain)
    dialog = Dialog(None, handler_chain)
    handler_chain = HelpHandler(topic_list[2], func, handler_chain)
    button = Button(dialog, handler_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')

其執行:

>>> demo()
Help with Paper orientation topic
Help with Print topic
Help with Application topic
No help with No such topic
Remove ads

Java

下面是Java的日誌類(logging)例子演示了該模式。 每一個logging handler首先決定是否需要在該層做處理,然後將控制傳遞到下一個logging handler。程序的輸出是:

  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就不會傳遞下去。在我們這個例子中,消息會一直傳遞到最底層不管它是否已經被處理。

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);
    }
}
Remove ads

引用

外部連結

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads