Паттерн Chain of Responsibility (Цепочка обязанностей) позволяет передавать запрос по цепочке обработчиков, где каждый обработчик решает, обрабатывать ли запрос или передать его дальше. Это помогает избавиться от жёсткой привязки отправителя запроса к его получателю.
Когда применять Chain of Responsibility
- Когда нужно, чтобы несколько объектов могли обрабатывать один и тот же запрос.
- Когда неизвестно, какой объект должен обработать запрос.
- Когда нужно избежать жёсткой связи между отправителем и получателем запроса.
PlantUML-диаграмма
// Абстрактный обработчик
public abstract class Handler
{
protected Handler _next;
public Handler SetNext(Handler next)
{
_next = next;
return next;
}
public abstract void Handle(string request);
}
// Конкретные обработчики
public class AuthHandler : Handler
{
public override void Handle(string request)
{
if (request == "AUTH")
{
Console.WriteLine("Проверка авторизации прошла успешно.");
}
else if (_next != null)
{
_next.Handle(request);
}
}
}
public class LogHandler : Handler
{
public override void Handle(string request)
{
Console.WriteLine($"Логируем запрос: {request}");
_next?.Handle(request);
}
}
public class DataHandler : Handler
{
public override void Handle(string request)
{
if (request == "DATA")
{
Console.WriteLine("Обработка данных...");
}
else
{
Console.WriteLine("Запрос не распознан.");
}
}
}
// Клиентский код
class Program
{
static void Main()
{
// Формируем цепочку: логирование → авторизация → обработка данных
var logger = new LogHandler();
var auth = new AuthHandler();
var data = new DataHandler();
logger.SetNext(auth).SetNext(data);
// Тестируем
logger.Handle("AUTH");
logger.Handle("DATA");
logger.Handle("UNKNOWN");
}
}
type Handler interface {
Handle(request string)
SetNext(next Handler) Handler
}
// Общая часть цепочки выносится встраиванием, а не наследованием.
type base struct {
next Handler
}
func (b *base) SetNext(next Handler) Handler {
b.next = next
return next
}
func (b *base) handleNext(request string) {
if b.next != nil {
b.next.Handle(request)
}
}
type AuthHandler struct{ base }
func (h *AuthHandler) Handle(request string) {
if request == "AUTH" {
fmt.Println("Проверка авторизации прошла успешно.")
return
}
h.handleNext(request)
}
type LogHandler struct{ base }
func (h *LogHandler) Handle(request string) {
fmt.Printf("Логируем запрос: %s\n", request)
h.handleNext(request)
}
type DataHandler struct{ base }
func (h *DataHandler) Handle(request string) {
if request == "DATA" {
fmt.Println("Обработка данных...")
} else {
fmt.Println("Запрос не распознан.")
}
}
func main() {
logger := &LogHandler{}
auth := &AuthHandler{}
data := &DataHandler{}
logger.SetNext(auth).SetNext(data)
logger.Handle("AUTH")
logger.Handle("DATA")
logger.Handle("UNKNOWN")
}
from abc import ABC, abstractmethod
class Handler(ABC):
def __init__(self) -> None:
self._next: Handler | None = None
def set_next(self, handler: "Handler") -> "Handler":
self._next = handler
return handler
def _handle_next(self, request: str) -> None:
if self._next is not None:
self._next.handle(request)
@abstractmethod
def handle(self, request: str) -> None: ...
class AuthHandler(Handler):
def handle(self, request: str) -> None:
if request == "AUTH":
print("Проверка авторизации прошла успешно.")
else:
self._handle_next(request)
class LogHandler(Handler):
def handle(self, request: str) -> None:
print(f"Логируем запрос: {request}")
self._handle_next(request)
class DataHandler(Handler):
def handle(self, request: str) -> None:
if request == "DATA":
print("Обработка данных...")
else:
print("Запрос не распознан.")
logger = LogHandler()
auth = AuthHandler()
data = DataHandler()
logger.set_next(auth).set_next(data)
logger.handle("AUTH")
logger.handle("DATA")
logger.handle("UNKNOWN")
abstract class Handler {
protected next: Handler | null = null;
setNext(next: Handler): Handler {
this.next = next;
return next;
}
abstract handle(request: string): void;
}
class AuthHandler extends Handler {
handle(request: string): void {
if (request === "AUTH") {
console.log("Проверка авторизации прошла успешно.");
} else {
this.next?.handle(request);
}
}
}
class LogHandler extends Handler {
handle(request: string): void {
console.log(`Логируем запрос: ${request}`);
this.next?.handle(request);
}
}
class DataHandler extends Handler {
handle(request: string): void {
if (request === "DATA") {
console.log("Обработка данных...");
} else {
console.log("Запрос не распознан.");
}
}
}
const logger = new LogHandler();
const auth = new AuthHandler();
const data = new DataHandler();
logger.setNext(auth).setNext(data);
logger.handle("AUTH");
logger.handle("DATA");
logger.handle("UNKNOWN");
abstract class Handler {
protected Handler next;
Handler setNext(Handler next) {
this.next = next;
return next;
}
abstract void handle(String request);
}
class AuthHandler extends Handler {
void handle(String request) {
if (request.equals("AUTH")) {
System.out.println("Проверка авторизации прошла успешно.");
} else if (next != null) {
next.handle(request);
}
}
}
class LogHandler extends Handler {
void handle(String request) {
System.out.println("Логируем запрос: " + request);
if (next != null) {
next.handle(request);
}
}
}
class DataHandler extends Handler {
void handle(String request) {
if (request.equals("DATA")) {
System.out.println("Обработка данных...");
} else {
System.out.println("Запрос не распознан.");
}
}
}
public class Program {
public static void main(String[] args) {
Handler logger = new LogHandler();
Handler auth = new AuthHandler();
Handler data = new DataHandler();
logger.setNext(auth).setNext(data);
logger.handle("AUTH");
logger.handle("DATA");
logger.handle("UNKNOWN");
}
}
abstract class Handler {
protected var next: Handler? = null
fun setNext(next: Handler): Handler {
this.next = next
return next
}
abstract fun handle(request: String)
}
class AuthHandler : Handler() {
override fun handle(request: String) {
if (request == "AUTH") {
println("Проверка авторизации прошла успешно.")
} else {
next?.handle(request)
}
}
}
class LogHandler : Handler() {
override fun handle(request: String) {
println("Логируем запрос: $request")
next?.handle(request)
}
}
class DataHandler : Handler() {
override fun handle(request: String) {
if (request == "DATA") {
println("Обработка данных...")
} else {
println("Запрос не распознан.")
}
}
}
fun main() {
val logger = LogHandler()
val auth = AuthHandler()
val data = DataHandler()
logger.setNext(auth).setNext(data)
logger.handle("AUTH")
logger.handle("DATA")
logger.handle("UNKNOWN")
}
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Ослабляет связь между отправителем и получателем запроса | Не гарантирует, что запрос будет обработан |
| Позволяет динамически изменять цепочку обработчиков | Может быть сложно отследить, где именно был обработан запрос |
| Упрощает добавление новых обработчиков | Может привести к длинной цепочке вызовов |
Заключение
Chain of Responsibility — поведенческий паттерн, позволяющий создавать гибкие цепочки обработчиков запросов. Он хорошо подходит для реализации систем фильтрации, валидации, middleware и событийных систем, где важно гибко управлять порядком и логикой обработки данных.