Design Patterns in Ruby: Adapter
12:13 PMDevelopment, Metodologies, RubyStefano
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #models.rb 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #models.rb 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:
1 2 3 4 5 6 7 8 9 10 11 | #adapters.rb 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:
1 2 3 4 5 6 7 8 9 10 11 | #main.rb 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #main2.rb 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.
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 | #main3.rb 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
Excellent door hangers online printing available at PsPrint
Tags: design patterns, ruby

















[...] ORIGINAL POST [...]
[...] This post was mentioned on Twitter by Jérémie Anderlin, Stefano Mancini. Stefano Mancini said: My new article on design patterns in Ruby: Adapter. http://ping.fm/fADFu [...]
I linked to this post at the Drink Rails blog as one of the top Ruby on Rails related posts of the day.
Thanks so much Sohan!
Brilliant! I didn’t realise I had so much more to learn!