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.
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:
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:
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
Design Patterns in Ruby: Adapter
This second post of the series leaves for a moment the creational patterns and speaks about one of the most important structural pattern: the Adapter.
The purpose of an adapter is “to convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.”
Suppose therefore to have two classes, PalGame and NtscGame, that extend a superclass Game. These subclasses respectively expose two methods: play and run.
class Game
attr_accessor :title
def initialize(title)
@title = title
end
end
class PalGame < Game
def play
puts "I am the Pal version of #{@title} and I am running!"
end
end
class NtscGame < Game
def run
puts "I am the NTSC version of #{@title} and I am running!"
end
end
We then subclass a Console class with PalConsole and NtscConsole. These two classes are expected to talk with, respectively, games of kind PalGame and NtscGame.
class Console
end
class PalConsole < Console
def play_game(game)
game.play
end
end
class NtscConsole < Console
def run_game(game)
game.run
end
end
How can see, the method play_game of a PalConsole will call the play method of the game, the NtscConsole instead will invoke the run method.
Our goal is to let a PalConsole to run games of kind NtscGame.
Below we’ll follow the line traced by the GoF and we’ll build an Adapter class that provides the play method needed by the PalConsole’s interface:
class NtscToPalAdatper
attr_accessor :game
def initialize(game)
@game = game
end
def play
@game.run
end
end
As you can see, the adapter exposes a simple play method that calls the run method of the NTSCGame.
The following code:
require 'models.rb'
require 'adapters.rb'
console = PalConsole.new
final_fantasy = NtscGame.new("Final Fantasy")
adapter = NtscToPalAdatper.new(final_fantasy)
console.play_game(adapter)
will produce this output:
I am the NTSC version of Final Fantasy and I am running!
We see at this point some alternatives to further exploit the potential of Ruby.
One possibility is to take advantage of the Ruby’s “open classes”. The language makes possible to add methods to an already loaded class in this way:
require 'models.rb'
console = PalConsole.new
class NtscGame < Game
def play
run
end
# alternatively for this simple example we can define an alias:
# alias play run
end
final_fantasy = NtscGame.new("Final Fantasy")
double_dragon = NtscGame.new("Double Dragon")
console.play_game(final_fantasy)
console.play_game(double_dragon)
As we can see, we’ve added at runtime the method play for a NtscGame game.
The above code produces the following output:
I am the NTSC version of Final Fantasy and I am running!
I am the NTSC version of Double Dragon and I am running!
Note however that with this solution we added the play method to the entire class NtscGame. All instances that are created will be equipped with the play method.
This type of action is very risky because it could not guarantee compatibility with other libraries, for example because of name clash.
Another alternative would be to use the “singleton classes”.
Ruby makes it possible to change a single instance of a class, by creating an anonymous class as its superclass that implements the new defined method.
There are several possibilities to implement these singleton classes. Let’s see in the following snippet some implementations.
require 'models.rb'
console = PalConsole.new
#1 - creating a singleton class
final_fantasy = NtscGame.new("Final Fantasy")
def final_fantasy.play
run
end
console.play_game(final_fantasy)
#2 - adding methods opening the singleton class directly
winning_eleven = NtscGame.new("Winning Eleven")
class << winning_eleven
def play
run
end
end
console.play_game(winning_eleven)
#3 - adding methods from a module
thunderforce = NtscGame.new("Thunderforce")
module Foo
def play
run
end
end
thunderforce.extend(Foo)
console.play_game(thunderforce)
#4 - adding methods inside an instance_eval call
dragons_lair = NtscGame.new("Dragons Lair")
dragons_lair.instance_eval <<EOT
def play
run
end
EOT
console.play_game(dragons_lair)
All implementations will provide the same type of output:
I am the NTSC version of Final Fantasy and I am running!
I am the NTSC version of Winning Eleven and I am running!
I am the NTSC version of Thunderforce and I am running!
I am the NTSC version of Dragons Lair and I am running!
In this post we have therefore seen the usefulness of this design patterns and how Ruby allows the developer to “leave” the classic implementation suggested in order to better take advantage of the potentiality of the language.
source code here
Previous posts from this series:
Design Patterns in Ruby: Introduction
Design Patterns in Ruby: Abstract Factory