热门问题
时间线
聊天
视角
责任链模式
来自维基百科,自由的百科全书
Remove ads
责任链模式在物件导向程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。

结构

在上面的UML类图中,Sender
类不直接提及到特定的接收者类。Sender
转而提及到了Handler
接口,通过它来处理一个请求(handler.handleRequest()
),这使得Sender
独立于处理这个请求的接收者。Receiver1
、Receiver2
和Receiver3
类实现了Handler
接口,它们要么处理要么转发一个请求(依赖于运行时条件)。
UML序列图展示了运行时交互:在这个例子中,Sender
对象调用handleRequest()
于receiver1
对象之上(它具有类型Handler
)。receiver1
转发这个请求至receiver2
,它接着转发这个请求至receiver3
,它处理了这个请求。
代码示例
《设计模式》书中早于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的例子:
from abc import ABC, abstractmethod
class Handler(ABC):
@abstractmethod
def handle_request(self): pass
topic_list = [
'Application topic', 'Print topic',
'Paper orientation topic']
class HelpHandler(Handler):
def __init__(self, successor, topic, handler):
assert topic in topic_list
self.__topic = topic
self.__successor = successor
self.__handler = handler
def handle_request(self, topic):
if self.__topic == topic:
self.__handler(self, topic)
elif self.__successor is None:
print(f"No help with {topic}")
else:
self.__successor.handle_request(topic)
class Application():
def __init__(self, handler):
self.handler = handler
def handle_help(self, topic):
self.handler.handle_request(topic)
class Widget():
def __init__(self, parent, handler):
self.parent = parent
self.handler = handler
def handle_help(self, topic):
self.handler.handle_request(topic)
def draw(self): pass
class Dialog(Widget): pass
class Button(Widget): pass
def demo():
def func(self, topic):
print(f"Help with {topic}")
handler1 = HelpHandler(None, topic_list[0], func)
handler2 = HelpHandler(handler1, topic_list[1], func)
handler3 = HelpHandler(handler2, topic_list[2], func)
application = Application(handler1)
dialog = Dialog(None, handler2)
button = Button(dialog, handler3)
handler1 = handler2 = handler3 = None
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
下面是PHP的例子:
<?php
abstract class Logger {
const ERR = 3;
const NOTICE = 5;
const DEBUG = 7;
protected $mask;
protected $next; // The next element in the chain of responsibility
public function setNext(Logger $l) {
$this->next = $l;
return $this;
}
abstract public function message($msg, $priority);
}
class DebugLogger extends Logger {
public function __construct($mask) {
$this->mask = $mask;
}
public function message($msg, $priority) {
if ($priority <= $this->mask) {
echo "Writting to stdout: {$msg}\n";
}
if (false == is_null($this->next)) {
$this->next->message($msg, $priority);
}
}
}
class EmailLogger extends Logger {
public function __construct($mask) {
$this->mask = $mask;
}
public function message($msg, $priority) {
if ($priority <= $this->mask) {
echo "Sending via email: {$msg}\n";
}
if (false == is_null($this->next)) {
$this->next->message($msg, $priority);
}
}
}
class StderrLogger extends Logger {
public function __construct($mask) {
$this->mask = $mask;
}
public function message($msg, $priority) {
if ($priority <= $this->mask) {
echo "Writing to stderr: {$msg}\n";
}
if (false == is_null($this->next)) {
$this->next->message($msg, $priority);
}
}
}
class ChainOfResponsibilityExample {
public function __construct() {
// build the chain of responsibility
$l = new DebugLogger(Logger::DEBUG);
$e = new EmailLogger(Logger::NOTICE);
$s = new StderrLogger(Logger::ERR);
$e->setNext($s);
$l->setNext($e);
$l->message("Entering function y.", Logger::DEBUG); // handled by DebugLogger
$l->message("Step1 completed.", Logger::NOTICE); // handled by DebugLogger and EmailLogger
$l->message("An error has occurred.", Logger::ERR); // handled by all three Loggers
}
}
new ChainOfResponsibilityExample();
?>
Remove ads
引用
外部链接
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads