Статья
Шаблон проектирования "Цепочка обязанностей"

Шаблон проектирования "Цепочка обязанностей"

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/