Autumn SALE
Łańcuch zobowiązań

Łańcuch zobowiązań w języku Ruby

Łańcuch zobowiązań to behawioralny wzorzec projektowy pozwalający przekazywać żądanie wzdłuż łańcucha potencjalnych obiektów obsługujących aż zostanie obsłużone.

W łańcuchu zobowiązań wiele obiektów może obsłużyć żądanie bez konieczności sprzęgania klas wysyłających je z konkretnymi klasami odbierającymi. Łańcuch można układać dynamicznie w trakcie działania programu z dowolnych obiektów obsługujących, wyposażonych w standardowy interfejs obsługi żądań.

Złożoność:

Popularność:

Przykłady użycia: Łańcuch zobowiązań jest rzadkim rozwiązaniem w programach napisanych w Ruby, gdyż ma sens tylko w tym kodzie, w którym występują łańcuchy obiektów.

Identyfikacja: Wzorzec można rozpoznać na podstawie obecności behawioralnych metod jednej grupy obiektów pośrednio wywołujących analogiczne metody w innych obiektach za pośrednictwem wspólnego interfejsu.

Przykład koncepcyjny

Poniższy przykład ilustruje strukturę wzorca Łańcuch zobowiązań ze szczególnym naciskiem na następujące kwestie:

  • Z jakich składa się klas?
  • Jakie role pełnią te klasy?
  • W jaki sposób elementy wzorca są ze sobą powiązane?

main.rb: Przykład koncepcyjny

# The Handler interface declares a method for building the chain of handlers. It
# also declares a method for executing a request.
class Handler
  # @abstract
  #
  # @param [Handler] handler
  def next_handler=(handler)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  # @abstract
  #
  # @param [String] request
  #
  # @return [String, nil]
  def handle(request)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# The default chaining behavior can be implemented inside a base handler class.
class AbstractHandler < Handler
  # @return [Handler]
  attr_writer :next_handler

  # @param [Handler] handler
  #
  # @return [Handler]
  def next_handler(handler)
    @next_handler = handler
    # Returning a handler from here will let us link handlers in a convenient
    # way like this:
    # monkey.next_handler(squirrel).next_handler(dog)
    handler
  end

  # @abstract
  #
  # @param [String] request
  #
  # @return [String, nil]
  def handle(request)
    return @next_handler.handle(request) if @next_handler

    nil
  end
end

# All Concrete Handlers either handle a request or pass it to the next handler
# in the chain.
class MonkeyHandler < AbstractHandler
  # @param [String] request
  #
  # @return [String, nil]
  def handle(request)
    if request == 'Banana'
      "Monkey: I'll eat the #{request}"
    else
      super(request)
    end
  end
end

class SquirrelHandler < AbstractHandler
  # @param [String] request
  #
  # @return [String, nil]
  def handle(request)
    if request == 'Nut'
      "Squirrel: I'll eat the #{request}"
    else
      super(request)
    end
  end
end

class DogHandler < AbstractHandler
  # @param [String] request
  #
  # @return [String, nil]
  def handle(request)
    if request == 'MeatBall'
      "Dog: I'll eat the #{request}"
    else
      super(request)
    end
  end
end

# The client code is usually suited to work with a single handler. In most
# cases, it is not even aware that the handler is part of a chain.
def client_code(handler)
  ['Nut', 'Banana', 'Cup of coffee'].each do |food|
    puts "\nClient: Who wants a #{food}?"
    result = handler.handle(food)
    if result
      print "  #{result}"
    else
      print "  #{food} was left untouched."
    end
  end
end

monkey = MonkeyHandler.new
squirrel = SquirrelHandler.new
dog = DogHandler.new

monkey.next_handler(squirrel).next_handler(dog)

# The client should be able to send a request to any handler, not just the first
# one in the chain.
puts 'Chain: Monkey > Squirrel > Dog'
client_code(monkey)
puts "\n\n"

puts 'Subchain: Squirrel > Dog'
client_code(squirrel)

output.txt: Wynik działania

Chain: Monkey > Squirrel > Dog

Client: Who wants a Nut?
  Squirrel: I'll eat the Nut
Client: Who wants a Banana?
  Monkey: I'll eat the Banana
Client: Who wants a Cup of coffee?
  Cup of coffee was left untouched.

Subchain: Squirrel > Dog

Client: Who wants a Nut?
  Squirrel: I'll eat the Nut
Client: Who wants a Banana?
  Banana was left untouched.
Client: Who wants a Cup of coffee?
  Cup of coffee was left untouched.

Łańcuch zobowiązań w innych językach

Łańcuch zobowiązań w języku C# Łańcuch zobowiązań w języku C++ Łańcuch zobowiązań w języku Go Łańcuch zobowiązań w języku Java Łańcuch zobowiązań w języku PHP Łańcuch zobowiązań w języku Python Łańcuch zobowiązań w języku Rust Łańcuch zobowiązań w języku Swift Łańcuch zobowiązań w języku TypeScript