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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#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:

1
2
3
4
5
6
#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

Previous posts from this series:
Design Patterns in Ruby: Introduction
Design Patterns in Ruby: Abstract Factory
Design Patterns in Ruby: Adapter


Tags: , ,


About Stefano

Stefano Mancini is a co-founder of DevInterface.

After graduating in Computer Science, he first specialized in Java/J2EE development by participating in several international projects in the pharmaceutical and banking ambits.

Enthusiast of agile development, like SCRUM for project management and eXtreme Programming for code writing, he then moved to dynamic languages like Ruby and Python.

About DevInterface

We are an information and communication technology agency. Our mission is to provide web application development, design services and communication strategies. We specialize in building web applications with modern and efficient frameworks.

Related Post

6 Responses to “Design Patterns in Ruby: Chain of Responsibility”

  1. [...] This post was mentioned on Twitter by Stefano Mancini, DevInterface. DevInterface said: "Design Patterns in Ruby: Chain of ResponsibilityDesign Patterns in Ruby: Chain of Responsibility" – http://bit.ly/dgPRtE #rubytweets [...]

  2. Peroli Sivaprakasam says:

    Stefano,
    In a way this looks like a decorator pattern. Can you provide an example of one in Ruby?

    Thanks,
    Peroli Sivaprakasam

  3. Stefano says:

    Hi Peroli.

    The decorator pattern can be used do add at runtime features that a class needs.

    In this example we can add all required skills to the WebDeveloper class in this way:

    - create a module that adds behaviour
    - add it at runtime to the class

    the code should be like this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    #test_decorator.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

    module AddSkills

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

    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

      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

    provider = WebManager.new
    web_developer = WebDeveloper.new
    web_developer.extend(AddSkills)
    provider.next_in_chain(web_developer)

    provider.deliver_application

    and this is the new output:

    WebDeveloper: I’m designing the interface
    WebDeveloper: I’m building the application
    WebDeveloper: I’m writing the documentation
    WebDeveloper: I’m deploying the application
    WebManager: Application delivered

  4. Shouldn’t the method missing in the module call super, if @next is null? i.e.,

    def method_missing(method, *args, &block)
    if @next == nil
    puts “This request cannot be handled!”
    super
    return
    end
    @next.__send__(method, *args, &block)
    end

  5. dubek says:

    Have you seen Ruby’s built-in Delegator and SimpleDelgator? http://ruby-doc.org/core/classes/SimpleDelegator.html

Leave a Reply

Insert code beetween <code lang="ruby"> and </code>

Copyright 2012 DevInterface s.n.c.

DevInterface Blog is proudly powered by WordPress