Шаблон проектирования "Цепочка обязанностей"
24 апреля 2020Относится к поведенческим шаблонам.
По сути это набор обработчиков, которые по очереди получают запрос, а затем решают обрабатывать его или нет. Далее запрос передается дальше по цепочке.
Плюсы:
- уменьшаем зависимость между запросом и обработчиками.
Минусы:
- запрос может быть не обработан.
Пример на PHP
abstract class Logger
{
public const DEBUG = 1;
public const CRITICAL = 2;
public const NOTICE = 4;
protected array $allowedPriorities = [];
protected ?Logger $next;
public function __construct(array $allowedPriorities = [])
{
$this->allowedPriorities = $allowedPriorities;
}
abstract protected function writeMessage(string $message): void;
public function message(string $message, int $priority): void
{
if (in_array($priority, $this->allowedPriorities, true)) {
$this->writeMessage($message);
}
if ($this->getNext() !== null) {
$this->getNext()->message($message, $priority);
}
}
public function setNext(Logger $next): void
{
$this->next = $next;
}
protected function getNext(): ?Logger
{
return $this->next;
}
}
class ConsoleLogger extends Logger
{
protected function writeMessage(string $message): void
{
echo $message . PHP_EOL;
}
}
class FileLogger extends Logger
{
protected function writeMessage(string $message): void
{
$f = fopen("error.log", "a");
fwrite($f, $message . PHP_EOL);
fclose($f);
}
}
Использование:
$logger = new ConsoleLogger([Logger::NOTICE]);
$fileLogger = new FileLogger([Logger::CRITICAL, Logger::DEBUG]);
$logger->setNext($fileLogger);
$logger->message("Notice message", Logger::NOTICE);
$logger->message("Critical error", Logger::CRITICAL);
Пример на Go
package main
import (
"io"
"log"
"os"
)
const (
Debug = 1
Critical = 2
Notice = 3
)
type ILogger interface {
SetNext(logger ILogger)
Write(message string, priority int)
Execute(message string)
}
type Logger struct {
allowedPriorities []int
next ILogger
}
func (l Logger) SetNext(next ILogger) {
l.next = next
}
func (l Logger) Execute(_ string) {
}
func (l Logger) Write(message string, priority int) {
for _, v := range l.allowedPriorities {
if v == priority {
l.Execute(message)
break
}
}
if l.next != nil {
l.next.Write(message, priority)
}
}
type ConsoleLogger struct {
Logger
}
func (l ConsoleLogger) Execute(message string) {
io.WriteString(os.Stdout, message)
}
type FileLogger struct {
Logger
}
func (l FileLogger) Execute(message string) {
f, err := os.OpenFile("text.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Println(err)
}
defer f.Close()
logger := log.New(f, "", log.LstdFlags)
logger.Println(message)
}
func main() {
logger := ConsoleLogger{Logger{
allowedPriorities: []int {Notice, Critical, Debug},
}}
fileLogger := FileLogger{Logger{
allowedPriorities: []int {Critical},
}}
logger.SetNext(fileLogger)
logger.Write("Notice message", Notice)
logger.Write("Critical message", Critical)
}
Источники:
http://designpatternsphp.readthedocs.io/ru/latest/Behavioral/ChainOfResponsibilities/README.html
https://habrahabr.ru/company/mailru/blog/325492/