Mappa Via Marconi 20, Bussolengo (VR)
Email info@devinterface.com

Design Patterns in Ruby: Chain of Responsibility

Today's post discusses the first of the behavioral pattern shown by the GoF, the chain of responsibility. This pattern expects a series of commands to be executed and a set of objects capable to handle them. Each of these "handler" objects can send the command to the next handler in the chain if it is not able to carry it out. A mechanism also exists for adding new handler objects to the end of this chain. To show this pattern, we take as example a real situation very familiar to web developers. Suppose that the manager must deliver a new web project. To realize the entire project should be conducted several heterogeneous activities such as design the user interface, develop the application, write the user manual, deploy the application. The manager doesn't have all required skills but he can rely on a pool of developers. When the activity reaches a developer, this can solve it or, if he is unable to, send it to a colleague. In this way a chain of responsibility is formed, where each actor specializes in solving only certain types of requests. Let's see how to carry out this scenario in Ruby. Is therefore necessary that every element in the chain has the ability to "forward" the request to the next if he is not able to manage it. So we create a module that defines this common logic and removes code duplication.

#models.rb
module Chainable

  def next_in_chain(link)
    @next = link
  end

  def method_missing(method, *args, &block)
    if @next == nil
      puts "This request cannot be handled!"
      return
    end
    @next.__send__(method, *args, &block)
  end
end
As you can see, the next_in_chain method provides the next element in the chain. To meet demands that can not be managed, I've used the method_missing "pattern" : when the request can not be managed by the actor (e.g. the invoked method is not defined in the class), it will be forwarded to the next. Now define the players of our example:

#models.rb
class WebManager
  include Chainable

  def initialize(link = nil)
    next_in_chain(link)
  end
  
  def deliver_application
    design_interface
    build_application
    write_documentation
    deploy_application
    puts "#{self.class.to_s}: Application delivered"
  end

end

class WebDeveloper
  include Chainable

  def initialize(link = nil)
    next_in_chain(link)
  end

  def build_application
    puts "#{self.class.to_s}: I'm building the application"
  end

  def deploy_application
    puts "#{self.class.to_s}: I'm deploying the application"
  end

end

class WebDesigner
  include Chainable

  def initialize(link = nil)
    next_in_chain(link)
  end

  def design_interface
    puts "#{self.class.to_s}: I'm designing the interface"
  end

end

class TechnicalWriter
  include Chainable

  def initialize(link = nil)
    next_in_chain(link)
  end

  def write_documentation
    puts "#{self.class.to_s}: I'm writing the documentation"
  end

end
At this point we simulate our chain by running the following code:

#main.rb
require 'models.rb'

provider = WebManager.new(WebDeveloper.new(WebDesigner.new(TechnicalWriter.new)))
provider.deliver_application
provider.make_support
and the output will be: WebDesigner: I'm designing the interface WebDeveloper: I'm building the application TechnicalWriter: I'm writing documentation WebDeveloper: I'm deploying the application WebManager: Application delivered This request cannot be handled! In conclusion, the use of this pattern is useful when we deal with heterogeneous requests and we want to make sure that these are best handled by a specific handler. It can also be used when we run commands in sequence, where each element forwards the coming command to the next handler. An alternative to this pattern could be a decorator, able to add capacity to a specific handler. In this case a single handler will completely manage all requests. In these first three articles of the series we have analyzed the first pattern shown by the GoF for each category. In the coming articles I will no more follow the order set by the GoF but I'll analyze the most useful patterns to address the most common needs. Source code "here":http://github.com/devinterface/design_patterns_in_ruby Previous posts from this series: Design Patterns in Ruby: Introduzione Design Patterns in Ruby: Abstract Factory Design Patterns in Ruby: Adapter