热门问题
时间线
聊天
视角
責任鏈模式
来自维基百科,自由的百科全书
Remove ads
在物件導向程式設計裡,責任鏈模式(英語:chain-of-responsibility pattern),是一種行為型軟件設計模式,它包含了一些命令對象和一系列的處理對象。每一個處理對象決定它能處理哪些命令對象,它也知道如何將它不能處理的命令對象傳遞給該鏈中的下一個處理對象。還存在往該處理鏈的末尾添加新的處理對象的機制。

結構

在上面的UML類圖中,Sender類不直接提及到特定的接收者類。Sender轉而提及到了Handler接口,通過它來處理一個請求(handler.handleRequest()),這使得Sender獨立於處理這個請求的接收者。Receiver1、Receiver2和Receiver3類實現了Handler接口,它們要麼處理要麼轉發一個請求(依賴於運行時條件)。
UML序列圖展示了運行時交互:在這個例子中,Sender對象調用handleRequest()於receiver1對象之上(它具有類型Handler)。receiver1轉發這個請求至receiver2,它接着轉發這個請求至receiver3,它處理了這個請求。
代碼示例
《設計模式》書中早於C++98的例子的C++23實現[2]:
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;
}
Remove ads
下面是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}")
chain = HelpHandler(topic_list[0], func)
application = Application(chain)
chain = HelpHandler(topic_list[1], func, chain)
dialog = Dialog(None, chain)
chain = HelpHandler(topic_list[2], func, 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')
其執行:
>>> demo()
Help with Paper orientation topic
Help with Print topic
Help with Application topic
No help with No such topic
Remove ads
下面是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
引用
外部連結
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads
