Design Patterns in Ruby: Chain of Command

Posted by Chris on October 03, 2006

Chain of Responsibility:

bq. Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

p>. “GOF”:http://www.amazon.com/gp/product/0201633612/ p.223

In the book, the GoF mention how in Smalltalk it is possible to do a very different implementation using the @doesNotUnderstand@ mechanism. Ruby of course has a similar one in @method_missing@, so I thought that I would use this in an implementation example of Chain of Responsibility. There is also an alternative implementation beneath it that does not use @method_missing@.


# Generic implementation for Chain of Responsibility
module Chainable
def next_in_chain(link)
@next = link
end

def method_missing(method, *args, &block)
if @next == nil
puts “huh?”
return
end

@next.__send__(method, *args, &block)
end
end

class X
include Chainable

def initialize(link)
next_in_chain(link)
end

def do_x
puts “x”
end
end

class Y
include Chainable

def do_y
puts “y”
end
end

y1 = Y.new
x1 = X.new(y1)

x1.do_x
x1.do_y
x1.do_z

Now, I guess it might not be a good idea to define method_missing in a module (comment anyone?), so you could of course change it to let the classes handle that themselves. I also did this alternative implementation that does not use method_missing but is still fairly generic.


# Alternative implementation for Chain of Responsibility
module Chainable
def next_in_chain
nil
end

def handle_message(message, *args, &block)
if self.respond_to?(message)
self.__send__(message, *args, &block)
else
next_in_chain.handle_message(message, *args, &block) unless next_in_chain.nil?
end
end
end

class X
include Chainable

def initialize(link)
@next = link
end

def next_in_chain
@next
end

def do_x(x)
puts x
end
end

class Y
include Chainable

def do_y
yield ‘y’
end
end

y1 = Y.new
x1 = X.new(y1)

x1.handle_message(:do_x, ‘x’)
x1.handle_message(:do_y) {|y| puts y}
x1.handle_message(:do_z)

Trackbacks

Trackbacks are closed.

blog comments powered by Disqus