Skip to content

Instantly share code, notes, and snippets.

@OleksandrPoltavets
Last active March 21, 2024 18:59
Show Gist options
  • Save OleksandrPoltavets/adaa6a03cb491b4d8b3ceb0920a9097a to your computer and use it in GitHub Desktop.
Save OleksandrPoltavets/adaa6a03cb491b4d8b3ceb0920a9097a to your computer and use it in GitHub Desktop.
CoR-pattern

Коротке пояснення:

Ланцюжок обов’язків (CoR, Chain of Command, Chain of Responsibility) — це поведінковий патерн проектування, що дає змогу передавати запити послідовно ланцюжком обробників. Кожен наступний обробник вирішує, чи може він обробити запит сам і чи варто передавати запит далі ланцюжком.

Плюси та мінуси:

Плюси:

  • Зменшує залежність між клієнтом та обробниками.
  • Реалізує принцип єдиного обов’язку (SOLID - Single responsibility).
  • Реалізує принцип відкритості/закритості (SOLID - Open/Close principle).

Мінуси:

  • Запит може залишитися ніким не опрацьованим.

Приклад коду:

class Handler
  attr_accessor :successor

  def initialize(successor = nil)
    @successor = successor
  end

  def handle_request(request)
    if can_handle?(request)
      handle(request)
    elsif successor
      successor.handle_request(request)
    else
      puts "Request #{request} was left unhandled."
    end
  end

  private

  def can_handle?(request)
    raise NotImplementedError, 'You must implement the can_handle? method'
  end

  def handle(request)
    raise NotImplementedError, 'You must implement the handle method'
  end
end

class ConcreteHandlerA < Handler
  private

  def can_handle?(request)
    request == :help
  end

  def handle(request)
    puts "ConcreteHandlerA handled request #{request}"
  end
end

class ConcreteHandlerB < Handler
  private

  def can_handle?(request)
    request == :process
  end

  def handle(request)
    puts "ConcreteHandlerB handled request #{request}"
  end
end

# Клієнтський код
handler1 = ConcreteHandlerA.new
handler2 = ConcreteHandlerB.new(handler1)

handler2.handle_request(:process) # Буде оброблено ConcreteHandlerB
handler2.handle_request(:help)    # Буде передано до ConcreteHandlerA
handler2.handle_request(:unknown) # Не буде оброблено

У цьому прикладі створено базовий клас Handler, який визначає інтерфейс для обробки запитів та містить посилання на наступний обробник у ланцюгу (successor). Класи ConcreteHandlerA та ConcreteHandlerB реалізують логіку обробки для конкретних типів запитів. Якщо об'єкт не може обробити запит, він передає запит наступному обробнику у ланцюзі. Якщо жоден обробник не може опрацювати запит, то запит залишається ніким не опрацьованим.

Аналогія у житті: У ресторані ви робите специфічний запит офіціанту, який передає його від кухаря до помічника, поки не знайдеться той, хто може задовольнити ваші смакові уподобання, подібно до того, як у патерні "Ланцюжок обов'язків" запит передається від одного обробника до іншого, поки не буде знайдено рішення.

Аналогія у Ruby on Rails:

Middleware в Rails — це серія компонентів, які обробляють вхідні HTTP-запити по ланцюгу, де кожен компонент виконує певну роль, перш ніж передати запит наступному обробнику.

Реалізація в сторонніх бібліотеках:

Organizers for Interactor gem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment