Você está na página 1de 32

ExampleDesignPatternsInRuby

Examples of design patterns implemented in Ruby, a topic suggested by HughSasse.

"Each pattern describes a problem which occurs over and over again in our environment, and then
describes the core of the solution to that problem, in such a way that you can use this solution a million
times over, without ever doing it the same way twice." -- Christopher Alexander.

A pattern is usually described as an abstraction of a solution to a problem in some context. A pattern does not describe the "one true
way" to solve a particular problem. Often two patterns will describe opposite solutions to a problem; the most suitable choice
depends on the context in which the problem occurs. Similarly, there is no one true way to implement a pattern; the pages below
offer examples not code templates. In practice, the design of single class will involve the use of multiple documented patterns and
some project specific patterns. The design and implementation of the class will therefore combine the roles of the patterns it
participates in, rather than be, for example, "a composite" or "a visitor".
Patterns

• AbstractFactoryPattern
• AbstractSessionPattern
• AdaptorPattern
• BouncerPattern
• ChainOfResponsibilityPattern
• CommandPattern
• CompositePattern?
• DecoratorPattern
• DelegatorPattern
• FactoryMethodPattern
• FlyweightPattern
• IteratorPattern
• NullObjectPattern
• ObserverPattern
• ProductTraderPattern
• ProxyPattern
• RepositoryPattern
• SingletonPattern
• StatePattern
• TemplateMethodPattern
• VisitorPattern

Almost all of these examples are correct Ruby programs. If example output is shown, you should be able to copy code from the web
page, run it in a Ruby interpreter and get the documented output. If you cannot run the examples, please let NatPryce know.

AbstractFactoryPattern
"Provide an interface for creating families of related or dependent objects without specifying their concrete classes." [1]
Ruby automatically implements the Abstract Factory pattern as Class objects. All class objects have the same interface: the new
method of each class object creates new instances of that class. Therefore code can pass references to class objects around and
they can be used polymorphically; code calling the new method of a class object does not need to know the exact type of object that
the class creates.

class Foo; end


class Bar; end

# Here is the use of the Abstract Factory pattern


def create_something( factory )
new_object = factory.new
puts "created a new #{new_object.class} with a factory"
end

# Here we select a factory to use


create_something( Foo )
create_something( Bar )

Running the code above results in the output:


created a Foo with a factory
created a Bar with a factory

Ruby's blocks and Proc objects can also be used as factories. A block can create and return a new object. Code that uses the factory
can yield to the block, or call the proc, every time a new object is needed.

def create_something_with_block
new_object = yield
puts "created a #{new_object.class} with a block"
end

def create_something_with_proc( &proc )


new_object = proc.call
puts "created a #{new_object.class} with a proc"
end

create_something_with_block { Foo.new }
create_something_with_block { Bar.new }
create_something_with_proc { Foo.new }
create_something_with_proc { Bar.new }

This produces the output:

created a Foo with a block


created a Bar with a block
created a Foo with a proc
created a Bar with a proc

Q: Ummm. Seems to me that you're specifying the concrete class name here: create_something(Foo) and
create_something(Bar). My understanding of Abstract Factory is that there's an additional level of indirection involved.

A: The create_something method is creating objects through an abstract interface and does not have knowledge of concrete types.
The code at the top level is selecting which factory object will be used by create_something. There always needs to be some part of
the code that creates factories, and that part needs knowledge of concrete types. The use of the Abstract Factory method is to shield
the rest of the code from that knowledge.

I found interesting to redo the maze example from the DP book in ruby. Basically, just translated it is:

class MazeGame
def create_maze(factory)
maze = factory.new_maze
room1 = factory.new_room 1
room2 = factory.new_room 2
door = factory.new_door room1, room2
maze.add room1
maze.add room2
room1.north= factory.new_wall
room1.east= door
room1.south= factory.new_wall
room1.west= factory.new_wall

room2.north= factory.new_wall
room2.east= factory.new_wall
room2.south= factory.new_wall
room1.west= door
return maze
end
end

Obviously, the new_xxx are simple to think as Xxx.new, as pointed above in this page, and the instance of a Factory can just
become a class or module holding the various Xxx:

module DwemthyDungeon
class Maze
...
end
end
class MazeGame
def create_maze (factory)
maze = factory::Maze.new
end

At the same time, the argument passing of "factory" becomes useless, since we can just assign it to a constant, say:

class MazeGame
def create_maze
maze = Factory::Maze.new
end

MazeGame::Factory=DwemthyDungeon

But then, again, The whole Factory thing becomes useless once we have a module:

class MazeGame
def create_maze
maze = Maze.new
end
...
end

MazeGame.send :include, DwemthyDungeon

As someone said, often patterns are invisible in ruby :) --GabrieleRenzi?

If you want more information, there are some good examples of the AbstractFactoryPattern at
http://rubycolor.org/dp/AbstractFactory.en.html

AbstractSessionPattern
"The Abstract Session pattern provides a way for an object to store per-client state without sacrificing type-safety or efficiency. A
service object, rather than providing a client with a handle to be passed as an argument to the operations of its abstract interface
instead creates an intermediate "session" object and returns a pointer to the session object back to the client. The session object
encapsulates the state information for the client which owns the session and is only exposed to the client as an abstract interface
through which the client can access the service's functionality with full type-safety." [1]
Type safety is not an issue in Ruby, a language that is strongly but dynamically typed. However, the use of per-client sessions is still
a very useful pattern when objects need to maintain different state for each of their callers. Here is a simplistic example:

class Server
def initialize
@client_id = 0
end

def session
@client_id += 1
Session.new( self, @client_id )
end

def service_client( session )


puts "servicing client #{session.client_id}"
end
end

class Session
attr :client_id

def initialize( server, client_id )


@server = server
@client_id = client_id
end
def service
@server.service_client( self )
end
end

server = Server.new
client1 = server.session
client2 = server.session

client1.service
client2.service

Running this program results in the output:

servicing client 1
servicing client 2

AdaptorPattern
"Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise
because of incompatible interfaces." [1]
There are two ways to implement the Adaptor pattern in Ruby: through delegation or through Ruby's open classes. The former is the
most common implementation of the Adapter pattern and is the most general. The latter is more concise but is not always possible.
In demonstrating each type of adaption we will use the following contrived example. We want to put a SquarePeg into a RoundHole
by passing it to the hole's peg_fits? method. The peg_fits? method checks the radius attribute of the peg, but a SquarePeg does
not have a radius. Therefore we need to adapt the interface of the SquarePeg to meet the requirements of the RoundHole.

class SquarePeg
attr_reader :width

def initialize( width )


@width = width
end
end

class RoundPeg
attr_reader :radius

def initialize( radius )


@radius = radius
end
end

class RoundHole
attr_reader :radius

def initialize( r )
@radius = r
end

def peg_fits?( peg )


peg.radius <= radius
end
end

Adaption by Delegation
To implement an Adaptor with delegation, create a new adaptor class that implements the required interface by making calls to the
adapted object.

class SquarePegAdaptor
def initialize( square_peg )
@peg = square_peg
end

def radius
Math.sqrt(((@peg.width/2) ** 2)*2)
end
end

hole = RoundHole.new( 4.0 )


4.upto(7) do |i|
peg = SquarePegAdaptor.new( SquarePeg.new(i.to_f) )
if hole.peg_fits?( peg )
puts "peg #{peg} fits in hole #{hole}"
else
puts "peg #{peg} does not fit in hole #{hole}"
end
end

This produces the output

peg #<SquarePegAdaptor:0xa038b10> fits in hole #<RoundHole:0xa038bd0>


peg #<SquarePegAdaptor:0xa038990> fits in hole #<RoundHole:0xa038bd0>
peg #<SquarePegAdaptor:0xa0388a0> does not fit in hole #<RoundHole:0xa038bd0>
peg #<SquarePegAdaptor:0xa038720> does not fit in hole #<RoundHole:0xa038bd0>

Adaption through Open Classes


Ruby's open classes let you add the required methods to the class that you want to adapt. This approach is far more concise than
the use of adaptor classes, and also has the advantage that you don't need to create another object.

class SquarePeg
def radius
Math.sqrt( ((width/2) ** 2) * 2 )
end
end

hole = RoundHole.new( 4.0 )


4.upto(7) do |i|
peg = SquarePeg.new(i.to_f)
if hole.peg_fits?( peg )
puts "peg #{peg} fits in hole #{hole}"
else
puts "peg #{peg} does not fit in hole #{hole}"
end
end

This produces the output:

peg #<SquarePeg:0xa038618> fits in hole #<RoundHole:0xa038b88>


peg #<SquarePeg:0xa0384b0> fits in hole #<RoundHole:0xa038b88>
peg #<SquarePeg:0xa0383d8> does not fit in hole #<RoundHole:0xa038b88>
peg #<SquarePeg:0xa038270> does not fit in hole #<RoundHole:0xa038b88>

This approach is not possible if the adapted object and required interface both have a method with the same name, but different
semantics. For example, if the RoundPeg class also had an width attribute that returned the diameter of the peg and was checked by
the RoundHole's peg_fits? method it would have the same name as, but a different meaning from, the width parameter of the
SquarePeg class. There would be no way of implementing both the RoundPeg and the SquarePeg interfaces in the same class.

Generalized adaptor
I've put some information on what I consider a generalized adaptor (one that lets you choose which interface you want to use) in
AdaptorPattern/Generalized. -- MauricioFernandez

BouncerPattern
"a bouncer is a method whose raison d'être is to either throw an exception or do nothing." [1]
In practice, bouncer methods are most useful when checking the validity of input data before passing it on to other parts of the
program.
Bouncer methods can be used to check preconditions and postconditions of methods and class invariants. However, when used for
this purpose they add additional overhead to the code. It is better to use a good suite of unit tests for checking postconditions and
invariants, and unit tests with mock objects for checking preconditions.
# x_str and y_str are strings that the user has input
def divide_str( x_str, y_str )
check_valid_number( x_str )
check_valid_number( y_str )

x = x_str.to_f
y = y_str.to_f

check_not_zero( y )

x/y
end

def check_valid_number( str )


raise "not a number" if str =~ /\A[0-9]+(\.[0-9]+([eE][0-9]+)?)?\z/
end

def check_not_zero( n )
raise "zero divisor" if n == 0
end

ChainOfResponsibilityPattern
"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." [1]
Usual Implementation
The classes below model a line of conga dancers. A dancer in a conga line can shimmy or shake. The leader can both shimmy and
shake, while the dancers behind him can shimmy but not shake. (Yes, it is a rather contrived example!). The dancers behind the
leader use the Chain Of Responsibility pattern to pass the call to shake down the line until it reaches the leader. They also use the
Chain Of Responsiblity pattern to pass calls to shimmy down the line after having performed their shimmy; in this way they also act
as Decorators of the dancers in front of them (see DecoratorPattern).

class CongaDancer
attr_reader :next_in_line

def initialize( next_in_line )


@next_in_line = next_in_line
end

def shimmy
puts "shimmy"
next_in_line.shimmy
end

def shake
next_in_line.shake
end
end

class CongaLeader
def shimmy
puts "shimmy!"
end

def shake
puts "shake!"
end
end

line = CongaDancer.new( CongaDancer.new ( CongaLeader.new ) )


line.shimmy
line.shake

This program produces the output:

shimmy
shimmy
shimmy!
shake!

Generic Chain Of Responsibility


When implementing the Chain Of Responsibility pattern as above, all objects in the chain must have the same interface. Objects that
cannot perform a request must still implement a method to pass the request down the chain. However, sometimes it is not possible
to design a common interface for chain elements. This is often the case in frameworks that will be extended by other programmers.
Users of your framework will want to call methods of a chained object that they wrote, but the chain interface defined by the
framework does not support their methods.
Ruby's open classes can be used to solve this problem. Users of your framework could add their own methods to all the framework
classes and any other classes they need to add to the chain. This solution will get impractical as more classes with additional
methods are used in the same chain because the additional methods of each class will have to be added to all the other classes.
Ruby's method_missing method can be used to implement a generic Chain Of Responsibility by passing any message that is not
understood by a chained object down the chain.
Using our dancers example, we can add a new type of dancer that can do the conga and the hokie-cokie. With the following
implementation of the method_missing method, the other conga dancers can pass requests for the hokie-cokie down the chain
without needing explicit support for that dance.

class CongaDancer
def method_missing( name, *args )
next_in_line.__send__( name, *args )
end
end

class HokieCokieDancer < CongaDancer


def in
puts "in"
end

def out
puts "out"
end

def shake
puts "shake it all about"
next_in_line.shake
end
end

line = CongaDancer.new( HokieCokieDancer.new ( CongaLeader.new ) )


line.in
line.out
line.in
line.out
line.shake

This produces the output:

in
out
in
out
shake it all about
shake!

CommandPattern
"Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and
support undoable operations." [1]
Procs as Commands
Ruby implements the Command pattern with Proc objects. A Proc object represents a callable block of code that closes over the
variables in scope when it was created; Ruby Proc objects are more concise and convenient than Command objects as implemented
in most OO languages.
For example, here is a program that uses Proc objects to represent commands:
count = 0

commands = []
(1..10).each do |i|
commands << proc { count += i }
end

puts "count is initially #{count}"


commands.each { |cmd| cmd.call }
puts "did all commands, count is #{count}"

Running this program results in the output:

count is initially 0
did all commands, count is 55

Undo / Redoable Commands


Proc objects provide a call method with which you can invoke their operation. They provide no way to undo that operation. If you
want to both do and undo Command objects you will need to implement objects that provide "do" and "undo" methods. Luckily Proc
objects can still help you with this -- both the "do" and the "undo" operation can be specified using Procs and you still get all the
advantage of closures. Furthermore, using Procs you can implement a generic undo/redo command:

class Command
def initialize( do_proc, undo_proc )
@do = do_proc
@undo = undo_proc
end

def do_command
@do.call
end

def undo_command
@undo.call
end
end

count = 0

commands = []
(1..10).each do |i|
commands << Command.new( proc { count += i }, proc { count -= i } )
end

puts "count is initially #{count}"


commands.each { |cmd| cmd.do_command }
puts "did all commands, count is #{count}"
commands.reverse_each { |cmd| cmd.undo_command }
puts "undid all commands, count is #{count}"
commands.each { |cmd| cmd.do_command }
puts "redid all commands, count is #{count}"

Running this program results in the output:

count is initially 0
did all commands, count is 55
undid all commands, count is 0
redid all commands, count is 55

The Big Problem


Using Procs to represent commands works perfectly until you have to pass commands between address spaces or save commands to
a file and later load them back into a Ruby program. Proc objects cannot be saved or restored using the Marshal module, which
means that they cannot be sent to DRB servers or saved to a file.
If you need to persist command objects or pass them as parameters to remote objects you will have to explicitly implement each
command as a Ruby class.

I've found myself using setting up commands alot in web applications, so that I can dispatch to different commands based on which
"submit" button was clicked. Is this an extension to command, or is the dispatcher a seperate pattern?
Definitely a separate pattern, but one that uses and builds upon CommandPattern. See
http://www.c2.com/cgi/wiki?InheritDontBranch for more information.
I found http://www.c2.com/cgi/wiki?PolymorphismVsSelectionIdiom to address this issue....

DecoratorPattern
"Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending
functionality." [1]
Generic Decorators
Generic decorators can be written by using the method_missing method. Method_missing is called when an object receives a
message that it does not have a method for. The method_missing method can forward the message on to another object and wrap
additional behaviour around the forwarded call.
For example, here is a decorator that logs all method calls made to an object and all values returned from those calls:

class LogDecorator
def initialize( target )
@target = target
end

def method_missing( name, *args )


call = "#{@target}.#{name}(" + (args.map {|a|a.inspect}).join(",") + ")"
puts "calling #{call}"
result = @target.__send__( name, *args )
puts "returned #{result.inspect} from #{call}"
result
end
end

The following program uses the LogDecorator? to log calls to the ENV object holding the program's environment variables:

env = LogDecorator.new( ENV )


env["USER"] = "Mr. Ed"
puts "user = " + env["USER"]

This results in the output:

calling ENV.[]=("USER","Mr. Ed")


returned true from ENV.[]=("USER","Mr. Ed")
calling ENV.[]("USER")
returned "Mr. Ed" from ENV.[]("USER")
user = Mr. Ed

Maintaining Object Identity


Using decorator objects is convenient if you want to dynamically add and remove behaviour to an object. However, the decorator
object is not the same object as the decorated object, and this can cause problems if you want to decorate an object but maintain
object identity -- if, for example, the decorated object is used as a hash key elsewhere in the system.
Ruby lets you implement the decorator pattern in a way that maintains object identity by using singleton methods and method
aliases. First, use alias to give another name to any methods you want to decorate. Then define new, singleton methods with
original method names to add behaviour and call the aliases.
Here's a program that adds logging to the ENV object using this technique:

class << ENV


alias do_set []=
alias do_get []

def []=( key, value )


result = do_set(key, value )
puts "set #{self}[#{key.inspect}] = #{value.inspect}"
result
end

def []( key )


value = do_get( key )
puts "got #{self}[#{key.inspect}] == #{value.inspect}"
value
end
end

ENV["USER"] = "Dr. No"


puts "user = " + env["USER"]

This results in the output:

set ENV["USER"] = "Dr. No"


calling ENV.[]("USER")
got ENV["USER"] == "Dr. No"
returned "Dr. No" from ENV.[]("USER")
user = Dr. No

This technique has some disadvantages over the use of delegation:


Firstly, it's harder to remove decorators created with this technique than decorators that use delegation. To remove a delegating
decorator, a program just stops referencing it and starts referencing the decorated object again; the garbage collection will then
reclaim the decorator. With this technique, a program has to undefine the decorator methods and rename the aliases back to their
originals names.
Secondly, it is hard to add more than one decorator to an object. When adding a second decorator to the example above, the alias
statements would replace the original definitions of [] and []= with the decorator methods, causing infinite recursion whenever they
were called.

You can use Object#extend to get around the second problem, although you can't add the same decorator to an object twice.
Example:

class Foo
def initialize(s)
@s = s
end
def to_s
@s
end
end

module SingleQuoted
def to_s
"'" + super + "'"
end
end

module DoubleQuoted
def to_s
'"' + super + '"'
end
end

foo = Foo.new('foo')
puts "#{foo.id} - #{foo}"
foo.extend(SingleQuoted)
puts "#{foo.id} - #{foo}"
foo.extend(DoubleQuoted)
puts "#{foo.id} - #{foo}"

This results in output similar to:

84006168 - foo
84006168 - 'foo'
84006168 - "'foo'"

JasonArhart
FactoryMethodPattern
"Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer
instantiation to subclasses. " [1]
I'll use the Factory Method pattern to show how to test object protocols. An object protocol is an abstract specification of an object's
interface, comprised of the methods that an object should respond to how it should behave in response to those methods. Ruby has
no way to specify or check object protocols in the language. It is up to the programmer to document protocols and to test protocol
implementations with unit tests.
An example of an object protocol is the behaviour of the methods eq? and hash -- if two objects are equal when compared by the
eq? method then they must return the same values from their hash method. Derived classes that redefine these methods must
conform to the protocol, or they cannot be used as keys in a hash table. A unit test for any class that redefines eq? and/or hash
should test that the class conforms to this protocol. Using the Factory Method pattern, these tests can be written as a reusable
module that is mixed into any unit test class that needs them.
The following module defines the mixin. Classes including this module should implement the Factory Methods create_value_1 and
create_value_2 to create instances of the class being tested. All values returned by create_value_1 should be equal, all values
returned by create_value_2 should be equal, and all values returned by create_value_1 should not be equal to all values returned
from create_value_2.

module EqHashProtocolTests
def test_eq
o1a = create_value_1
o1b = create_value_1
o2 = create_value_2

assert( o1a.eq?(o1b) )
assert( o1b.eq?(o1a), "eq? not commutative on success" )
assert( !o1a.eq?(o2) )
assert( !o2.eq?(o1a), "eq? not commutative on failure" )
end

def test_hash
o1a = create_value_1
o1b = create_value_1

assert_equal( o1a.hash, o1b.hash )


end
end

Now we write a class that redefines eq? and hash. In this example, it is a class that represents TCP/IP addresses.

class TCPAddress
attr :host
attr :port

def initialize( host, port )


@host = host
@port = port
end

def eq?( a )
self == a
end

def ==( a )
a.host == @host && a.port == @port
end

def hash
@host.hash ^ @port.hash
end

def to_s
"#{host}:#{port}"
end
end

Now we can use the EqHashProtocolTests? mixin to test that the TCPAddress class conforms to the protocol governing the behaviour
of eq? and hash.
require 'runit/testcase'

class TestTCPAddress < RUNIT::TestCase


include EqHashProtocolTests

def create_value_1
TCPAddress.new( 'localhost', 22 )
end

def create_value_2
TCPAddress.new( 'www.b13media.com', 80 )
end

def test_to_s
assert_equal( "www.b13media.com:80",
TCPAddress.new( 'www.b13media.com', 80 ).to_s )
end
end

Now let's test that I implemented the TCPAddress class correctly.

require 'runit/cui/testrunner'

RUNIT::CUI::TestRunner.run( TestTCPAddress.suite )

Running this produces the output:

TestTCPAddress#test_eq .
TestTCPAddress#test_hash .
TestTCPAddress#test_to_s .
Time: 0.01
OK (3/3 tests 6 asserts)

Marvelous. I'll check that into the repository and get a coffee...
--NatPryce

IteratorPattern
"Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation." [1]
Ruby implements iterators with blocks and the 'each' method, and with 'for ... in' statements.
For example:

def print_elements( container )


container.each {|o| puts o.inspect }
end

list = [1,2,3,4]
hash = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
print_elements list
print_elements hash

Results in the output:

1
2
3
4
["a", 1]
["b", 2]
["c", 3]
["d", 4]

There is also some good coverage of the IteratorPattern at http://rubycolor.org/dp/Iterator.en.html


NullObjectPattern
"A recursive structure, like a list or a tree, requires some special marker to define those nodes at the structure's boundary. Also
interior and edge nodes should be as similar as possible since one can become the other as the structure grows or shrinks.
"Therefore: Mark the space just beyond the structure's edge with instances of an appropriate null object. Expect this object to
participate in calculations by returning zero, null or empty, as appropriate for any particular algorithm." [1]

class Tree
attr_accessor :left, :right, :value

def initialize( value )


@left = NullTree.new
@right = NullTree.new
@value = value
end

def size
1 + left.size + right.size
end

def sum_of_values
value + left.sum_of_values + right.sum_of_values
end

def product_of_values
value * left.product_of_values * right.product_of_values
end
end

class NullTree
def size
0
end

def sum_of_values
0
end

def product_of_values
1
end
end

tree = Tree.new( 2 )
tree.left = Tree.new( 3 )
tree.left.right = Tree.new( 4 )
tree.left.left = Tree.new( 5 )

p tree.size
p tree.sum_of_values
p tree.product_of_values

Results in the output:

4
14
120

As a wee optimization, you could use the SingletonPattern to implement the null class.

require 'singleton'
class NullTree
include Singleton
# ...
end
class Tree
def initialize( value )
@left = NullTree.instance
@right = NullTree.instance
@value = value
end
# ...
end

or even...

class Tree
@@empty_leaf = NullTree.new
def initialize
@left = @right = @@empty_leaf
...

In Ruby, the nil value is itself an example of the NullObjectPattern. You can send the message nil? to any object. The nil value
returns true and other objects return false.

I would make the NullTree?'s class a subclass of Tree and provide a predicate to check. Then all of the code for a method can be
implemented in Tree and people wanting to extend it only need to extend that.

class Tree; end

NullTree = Class.new(Tree) {
def dup() self; end
def clone() self; end
def null_tree?() true; end
def inspect() 'NullTree'; end
}.new

class Tree

def initialize(val)
@val = val
@left = @right = NullTree
end

def null_tree?() false; end

attr_accessor :val, :left, :right

def size
if null_tree?
0
else
1 + @left.size + @right.size
end
end

end

-- DevinPapineau?

ObserverPattern
"Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and
updated automatically." [rampages.onramp.net/~huston/dp/observer.html]
The Canonical Implementation
The Ruby library includes an implementation of the Observer pattern, as described in the Pickaxe Book
[www.rubycentral.com/book/lib_patterns.html]. This implementation follows the example in the GoF Design Patterns book:
observers are objects that respond to the update message and the observed object calls the update method of its observers
whenever it changes.
The Ruby Way
Ruby's Proc objects are a more convenient and concise way to implement the Observer pattern: an observed object maintains a list
of Procs, rather than Observer objects, and calls all the registered Procs when it changes. Procs are convenient because they have a
closure over the variables that were in scope when the Proc was created. Each time a Proc is called, it's body can use any of those
variables, including, most importantly, the observed object with which it was registered. Using the Observer pattern with closures is
a very powerful way to glue together loosley coupled objects.
Here's an example module that provides minimal Observable functionality using Procs:

module Observable
def on_change( &callback )
@observers = [] if not defined? @observers
@observers << callback
end

protected
def announce_change
@observers.each { |o| o.call } if defined? @observers
end
end

Here's an example class that defines Observable objects:

class Counter
include Observable

attr :count

def initialize( count = 0 )


@count = count
end

def inc
@count += 1
announce_change
end

def dec
@count -= 1
announce_change
end
end

And finally, here's an example program that uses a closure to react to changes to a Counter object:

counter = Counter.new
counter.on_change do
puts "counter now has value #{counter.count}"
end

counter.inc
counter.inc
counter.dec
counter.dec

Running this program produces the output:

counter now has value 1


counter now has value 2
counter now has value 1
counter now has value 0

Further Extensions
This implementation strategy can be extended further to implement events as used in JavaBeans?. Firstly, an object can provide
different methods with which clients can register for notification of different events. Secondly, an object can pass parameters to the
procs associated with different events.
This is how the Tk module announces user-input events.
The garbage collector uses this idiom to implement finalizers. Objects can register procs to be notified when another object has been
garbage collected.

Events
Here is an example implementation of events if the object has just one event just omit the event parameter and it will work with the
default nil value with little overhead to the previous examples.

module Observable
def add_observer(event=nil, &obj)
@observers = {} unless @observers
e = @observers[event]
if e
e << obj
else
@observers[event] = [obj]
end
obj
end
def remove_observer(obj,event=nil)
if @observers
l = @observers[event]
l.delete(obj) if l
end
obj
end
protected
def signal_event(event=nil)
if @observers
l = @observers[event]
l.each { |o| o.call self, event } if l
end
end
end

- Musasabi
Generalization of Events
You can further generalize this so objects can send arbitrary events with arbitrary arguments in a natural manner, similar to .NET
and the like:

class Module
def sends *args
args.each { |arg|
class_eval <<-CEEND
def on_#{arg}(&callback)
@#{arg}_observers ||= {}
@#{arg}_observers[caller[0]]=callback
return caller[0]
end
def del_#{arg}(id)
@#{arg}_observers ||= {}
return @#{arg}_observers.delete( id)
end
private
def #{arg} *the_args
@#{arg}_observers ||= {}
@#{arg}_observers.each { |caller, cb|
cb.call *the_args
}
end
CEEND
}
end
end

This would allow you to define events that an object would send, like so:

class TextBox
sends "text_changed", "key_pressed"

def initialize txt = ""


@txt = txt
end

def txt= txt


@txt = txt
text_changed
end

def key_press key


key_pressed key
end
end

This class will send text_changed and key_pressed messages to any proc registered with TextBox#on_text_changed or
TextBox#on_key_pressed, respectively, like so:

box = TextBox::new
text_changed_id=box.on_text_changed { puts "Text changed!" } # registering a callback
5.times{
box.on_key_pressed { |k| puts "Key pressed: #{k}" }
}
box.txt= "New text!"
box.del_text_changed( text_changed_id) # unregistering the callback
box.txt= "New text!"
box.key_press 'j'

I'm sure this could be obtimized, I made no attempts to do so.


-- Brian Palmer
I changed the callback lists (@..._observers) to hashes. which brings two advantages:
1. calling the register method (on_...) multiple times won't register the same callback more than once
2. unregistering a callback is possible if you keep the return value of the register function.
-- Henon
Btw: there is a neat implementation of the Observer Pattern in RAA: Observable by Joel Vanderwarf

ProductTraderPattern
"Let clients create objects by naming an abstract superclass and providing a specification." [1]
This differs from standard object creation (in which clients create objects by naming a concrete class), from creation using an
abstract factory (in which clients create objects by naming a concrete factory class and calling a predetermined method) and from
creation via a factory method (in which an object knows an appropriate type of object to create from an alternative hierarchy.)
Essentially, there are four main objects involved in the Product Trader pattern. The Specification is an object that describes the
product wanted by a client. The Product Trader creates a Product, having been given a Specification. The Product is an object
created by the Product Trader. The Creator is an object that performs the creation process for the Product Trader.

In order for a Product Trader to be able to create a Product from a Specification, there would usually have to be a mapping
from Specifications to Products within the Product Trader class. Typically, this would require that the product trader know all of
the concrete classes in advance. This could be done by writing code that explicitly registers products with product traders but that
does require extra housekeeping code that is separate from the products themselves and has to be kept in sync manually. However,
in a language such as Ruby, this is not necessary as products can dynamically register themselves with the product trader at
runtime. One way of doing this is by executing code at class definition time to call a register class method on the Product Trader
class, passing in self. The Product Trader can then perform the role of the creator itself when necessary, by calling the new
method of the class object previously registered with it.
Another advantage of Ruby is that Products can decide for themselves if they match a given specification. This requires that all of
the Products respond to a common 'match_spec' message, but as Ruby is dynamically-typed the products do not have to share a
common base class in order to achieve this. The onus is on the Product itself to decide whether the specification provided is a good
enough match for the services that it provides.
There are several kind of checks that could be made:

• Does the specification exactly match another object?


• Does the specification provided respond to a certain set of methods?
• Does the specification provided match a certain regular expression when it is converted to it's string representation?
An example of how this pattern could be used is as follows:
Given a file containing a list of events and date ranges on which these events take place, how can we convert these into objects that
can respond to queries as to whether a given date is a date on which the event occurs?
i.e.

Event: Buy Paper


Occurs: Saturday

Event: Postman comes


Occurs: Weekdays

Event: Barbecue
Occurs: June

Given a date, for example Saturday 7th June 2003, the first and the third events should respond true when asked if they occur on
the given date wheras the second event should respond false.
This could be achieved by each event having a member variable representing a range of dates on which the event could occur. By
using the StrategyPattern, the determination of whether a given date matches the specified range could be delegated to a specialist
object. At the point of loading the file (or indeed receiving the data from any other external source), the appropriate strategy object
could be created by using a Product Trader, based on the Specification string.

Here is an example of some code implementing the Product Trader pattern:

require 'Date'
require 'test/unit'

class DateMatchTrader
@@subclasses = []
def DateMatchTrader.register(subclass)
@@subclasses << subclass
end

def DateMatchTrader.get_product(spec)
matching = @@subclasses.find {|sc| sc.match_spec(spec) }
return matching.new(spec) if matching
nil
end
end

class WeekDays
DateMatchTrader.register(self)
def WeekDays.match_spec(spec)
return true if /WeekDays/.match(spec)
end

def initialize(spec)
end

def date_match(date)
return true if ((1..6).member?(date.wday))
false
end
end

class InMonth
DateMatchTrader.register(self)
MONTHS = ["January", "February", "March", "April", "May",
"June", "July", "August", "September",
"October", "November", "December"]
MONTHLY = /#{MONTHS.join("|")}/

def InMonth.match_spec(spec)
return true if MONTHLY.match(spec)
false
end

def initialize(spec)
@month = MONTHS.index(MONTHLY.match(spec).to_s) + 1
end

def date_match(date)
return true if (date.month == @month)
false
end
end

class Weekly
DateMatchTrader.register(self)
DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday"]
DAILY = /#{DAYS.join("|")}/

def Weekly.match_spec(spec)
return true if DAILY.match(spec)
false
end

def initialize(spec)
@weekday = DAYS.index(DAILY.match(spec).to_s)
end

def date_match(date)
return true if (date.wday == @weekday)
false
end
end

class DateMatchTraderTest < Test::Unit::TestCase


def setup
@sundayInJune = Date.new(2003, 6, 8)
@mondayInJune = Date.new(2003, 6, 9)
@tuesdayInJune = Date.new(2003, 6, 10)
@mondayInMay = Date.new(2003, 5, 12)
@tuesdayInMay = Date.new(2003, 5, 13)
end

def test_june
june = DateMatchTrader.get_product("June")
assert_equal(true, june.date_match(@mondayInJune))
assert_equal(true, june.date_match(@tuesdayInJune))
assert_equal(false, june.date_match(@mondayInMay))
end

def test_monday
monday = DateMatchTrader.get_product("Monday")
assert_equal(true, monday.date_match(@mondayInJune))
assert_equal(false, monday.date_match(@tuesdayInJune))
assert_equal(true, monday.date_match(@mondayInMay))
end

def test_weekday
weekday = DateMatchTrader.get_product("WeekDays")
assert_equal(false, weekday.date_match(@sundayInJune))
assert_equal(true, weekday.date_match(@mondayInJune))
end
end

ProxyPattern
"Provide a surrogate or placeholder for another object to control access to it." [1]
Ruby's method_missing method provides a way of implementing generic Proxy objects. Method_missing is called when an object
receives a message that it does not have a method for. The method_missing method can forward the message on to another object
and wrap additional behaviour around the forwarded call.
Here is an example proxy that forwards calls to an object in another process using TCP/IP.
require 'socket'

class Proxy
def initialize( host, port )
@host = host
@port = port
end

def type
@target.type
end

def method_missing( name, *args )


socket = TCPSocket.new( @host, @port )
begin
# Send request to server
Marshal.dump( name, socket )
Marshal.dump( args.length, socket )
args.each { |a| Marshal.dump( a, socket ) }
socket.flush

# Get reply from server


is_ok = Marshal.load( socket ) # will return a boolean
result = Marshal.load( socket )

if is_ok
# The server has returned the result of the remote method
return result
else
# The server has returned an exception
raise result
end

ensure
socket.close
end
end
end

For good measure, here is the server program:

require 'socket'

class Accumulator
def accumulate( *args )
total = 0
args.each { |a| total = total + a }
total
end
end

def dispatch_call( object, socket )


begin
method = Marshal.load( socket )
args = Array.new( Marshal.load( socket ) )
args.each_index { |i| args[i] = Marshal.load( socket ) }

result = object.__send__( method, *args )

Marshal.dump( true, socket )


Marshal.dump( result, socket )

rescue => ex
Marshal.dump( false, socket )
Marshal.dump( ex, socket )

ensure
socket.close
end
end

acc = Accumulator.new
server = TCPServer.new( '127.0.0.1', 54321 )
puts "waiting for connections on host 127.0.0.1, port 54321"
loop do
dispatch_call( acc, server.accept )
end

Here's a client program that uses the proxy to call a remote object:

proxy = Proxy.new( '127.0.0.1', 54321 )


puts proxy.accumulate( 1,2,3,4,5,6,7,8,9,10 )

Running the server and client programs results in this output from the client:

55

Distributed object invocation in 59 lines of code!

RepositoryPattern
A repository allows an application to query domain objects from an underlying data source. There are a number of ways to
implement the query itself. Specifically, this example uses the specification pattern, which provides Boolean based rule matching.
UML is more appropriate for class-based languages than prototype languages, such as Ruby. So the method signatures in the class
diagram require some explanation (in fact, even the name class diagram shows UML's bias against prototype languages). The
do_match methods require as a parameter some object that implements a song() method. There is no concept of a Song type. In
turn, the match policy objects themselves, which are contained within the array parameter of the SongRepository? method, are
objects that implement a do_match method. Again, no concept of type.

Take a look at SongTest?. This method invokes ArtistMatchPolicy?.new("Guided By Voices") (1) and
SongNameMatchPolicy?("Everyday") (2). So in this case, we want to insure that if there are two or more artists that happen to sing
the same song (Everyday), we get the one sung by Guided by Voices. The SongTest? then obtains the SongRepository? (3) and
invokes its get_song_for method, passing in an array of match policies (4), obtained in steps 1 and 2.
The SongRepository? object gets all of the Songs from the SongDAO?. In this case, the SongDAO? is a stub containing hard-code
values. In a production environment, this method would return songs from an underlying data source such as a database. Returning
all songs to the application and letting the application do the query is referred to as application level searching. There is some
debate as to the scalability of such a solution with valid points on both sides. It depends on your environment, the size of the data
(it does not work with objects containing BLOBs) and the complexity of the domain model.
The SongRepository? now passes in each song into each match policy. If one of the match policies returns false, then the entire song
is excluded from the returned array of songs. SongRepository? returns the array of songs that meet the match criteria, in this case
the songs "I'll Replace You With Machines" and "Viva! Sea-Tac" by Robyn Hitchcock have been filtered out.

# This object provides services for returning songs.


class SongRepository
# param matchPolicies an array that contains match policy objects. These objects are
# required to implement a do_match method that returns a
# boolean
# returns array of songs that meet the match criteria
def get_song_for(matchPolicies)
matchedSongs = Array.new
for i in SongDAO.new.get_all_songs
if(is_match(i, matchPolicies))
matchedSongs.push(i)
end
end
return matchedSongs
end

private
def is_match(song, matchPolicies)
for i in matchPolicies
if !i.do_match(song)
return false
end
end
return true
end
end

#A match policy for song names.


class SongNameMatchPolicy
def initialize(song_name)
@song_name = song_name
end

#returns true if the song name matches, otherwise returns false


def do_match(song)
return (song.name == @song_name)
end
end

#A match policy for the name of the artist.


class ArtistMatchPolicy
def initialize(artist)
@artist = artist
end

#returns true if the artist name matches, otherwise returns false.


def do_match(song)
return song.artist == @artist
end
end

#A value object for songs.


class Song
#Accessors for the name of the song and the name of the artist.
attr_reader :name, :artist

def initialize(artist, name)


@name = name
@artist = artist
end

def to_string
"Name = #{@name} , Artist = #{@artist}"
end
end

#Song data access object. In this case, it is just a stub containing hard-coded values.
class SongDAO
def initialize
@songs = [Song.new("Guided By Voices", "I'll Replace You With Machines"),
Song.new("Guided By Voices", "Everyday"),
Song.new("Robyn Hitchcock", "Viva! Sea-Tac")]
end

#returns all songs


def get_all_songs
@songs
end
end

#Test class for the song repository.


class SongTest
def initialize
@songNameMatchPolicy = SongNameMatchPolicy.new("Everyday")
@artistMatchPolicy = ArtistMatchPolicy.new("Guided By Voices")
@repository = SongRepository.new
end

#Requires a match of both of the song name and artist name.


def test_song_and_artist
for i in @repository.get_song_for([@songNameMatchPolicy,
@artistMatchPolicy])
puts i.to_string
end
end

#Matches all the songs from an artist.


def test_artist
for i in @repository.get_song_for([@artistMatchPolicy])
puts i.to_string
end
end
end

songTest = SongTest.new
songTest.test_song_and_artist
songTest.test_artist

SingletonPattern
"Ensure a class has only one instance, and provide a global point of access to it." [1]
The Ruby library includes an implementation of the Singleton pattern. See the Pickaxe Book [2] for more details.
However, modules can also be used as singleton objects. A module is implemented in Ruby as an object. A module's
module_functions are implemented as instance methods of the module object and the module's state is implemented as instance
variables of the module object. You can therefore pass a reference to the module around as you would with any other object, and
Ruby automatically gives it a global point of access.

If you can create the singleton object at load time, then you can probably do this instead of module_function:

MyObject = Object.new
def MyObject.foo; ...; end
def MyObject.bar; ...; end

or:

MyObject = Object.new
class << MyObject
def foo; ...; end
def bar; ...; end
end

and you can do basically the same using global variables too.
-- MatjuBoo?
Defining module_functions is just a complicated way of defining singleton methods of a Module object. For example,

module Mod
def eins;end
def zwei;end
module_function :eins,:zwei
end

is just an abbreviation for

Mod = Module.new
class << Mod
def eins;end
def zwei;end
end
module Mod
private
def eins;end
def zwei;end
end

Come to think of it, you only need full-blown OO-singletons when you don't have full control about instanciation time - i.e. the first
call of

SomeClass.instance

- otherwise Ruby's singleton objects, accessible via constants or global variables, will do just fine.
--Chr. Rippel

StatePattern
"Allow an object to alter its behavior when its internal state changes. The object will appear to change its class." [1]
To allow the state object to change the state of the context without violating encapsulation, an interface to the outside world can be
wrapped around a context object.

class Client
def initialize
@context = Context.new
end
def connect
@context.state.connect
end
def disconnect
@context.state.disconnect
end
def send_message(message)
@context.state.send_message(message)
end
def receive_message
@context.state.receive_message
end
private
class Context
def initialize
@state = Offline.new(self)
end
attr_accessor :state
end
end

class ClientState
def initialize(context)
@context = context
inform
end
end

class Offline < ClientState


def inform
puts "offline"
end
def connect
@context.state = Online.new(@context)
end
def disconnect
puts "error: not connected"
end
def send_message(message)
puts "error: not connected"
end
def receive_message
puts "error: not connected"
end
end

class Online < ClientState


def inform
puts "connected"
end
def connect
puts "error: already connected"
end
def disconnect
@context.state = Offline.new(@context)
end
def send_message(message)
puts "\"#{message}\" sent"
end
def receive_message
puts "message received"
end
end

client = Client.new
client.send_message("Hello")
client.connect
client.send_message("Hello")
client.connect
client.receive_message
client.disconnect

Running the code above results in the output:

offline
error: not connected
connected
"Hello" sent
error: already connected
message received
offline

-- JasonArhart
Q: What is the purpose of the Context object ? Couldn't the Client hold a ClientState? object ?

A: Encapsulation. The Context object is the real object. The Client object is just a wrapper that protects the state from being
changed directly.
[I believe this does not answer the question. Encapsulation is kept if the object of class Client holds a reference to a ClientState?
object:
class Client
def initialize
@state = Offline.new
end

def method_missing(meth, *args, &block)


@state = @state.send(meth, *args, &block)
nil # dummy
end
end

This requires however every method to return the next state (instead of some useful return value).
So IMHO a better answer to the question would be: "so that the methods can return interesting values instead of the next state". --
MauricioFernandez]
Q: What is the relationship between the State pattern and the Delegator pattern ?
A: Delegation is the most common way of implementing the state pattern. You can implement delegation by hand (like in this
example) or use Ruby's Delegator and Forwardable modules. -- SimonVandemoortele

Q: Where should the state transition logic be ? In the state objects (ClientState?) or in the object whose state
changes (Context) ?

A: It depends on the situation. I refer you to the Design Patterns book by GHJV (aka the gang of four) for a discussion of the pro
and cons of both implementations. -- SimonVandemoortele

TemplateMethodPattern
"Define the skeleton of an algorithm in an operation, deferring some steps to client subclasses. Template Method lets subclasses
redefine certain steps of an algorithm without changing the algorithm's structure." [1]
class Accumulator
def accumulate( values )
total = initial_value
values.each { |v| total = accumulate_value( total, v ) }
total
end
end

class Sum < Accumulator


def initial_value
0
end
def accumulate_value( total, v )
total + v
end
end

class Product < Accumulator


def initial_value
1
end
def accumulate_value( total, v )
total * v
end
end

p Sum.new.accumulate( [1,2,3,4] )
p Product.new.accumulate( [1,2,3,4] )

Results in the output:

10
24

Ruby's singleton methods (not to be confused with the SingletonPattern) makes the Template Method pattern even more flexible.
Programs can define template methods on individual objects without needing to define an entire new class:

def accumulate_strings( list )


acc = Accumulator.new
def acc.initial_value
""
end
def acc.accumulate_value( total, v )
total + v
end

acc.accumulate( list )
end

p accumulate_strings( ["1", "2", "3", "4"] )

Results in the output:

"1234"

Blocks can also be used in place of the Template Method pattern, although this is now much closer to the StrategyPattern.

def accumulate_with_block( initial, list )


total = initial
list.each { |v| total = yield( total, v ) }
total
end

p accumulate_with_block( 0, [1,2,3,4] ) { |total,v| total + v }


p accumulate_with_block( 1, [1,2,3,4] ) { |total,v| total * v }

Results in the output:

10
24

VisitorPattern
"Visitor lets you define a new operation without changing the classes of the elements on which it operates." [1]
The primary purpose of the Visitor pattern is to double dispatch on both the class of object implementing an operation and the
classes of the elements being operated upon. Since Ruby implements single-dispatch polymorphism, the each method does not do
enough to implement the Visitor pattern.
Canonical Visitor Example
Here's how the visitor pattern would normally be implemented in a single-dispatch language. However, Ruby has another, easier
way to achieve the same end (see below).
To start, lets define a simple class hierarchy defining shapes (rectangle, line) and graphical composition (group)

class Shape
end

class Rectangle < Shape


attr_accessor :x, :y, :width, :height

def initialize( x, y, width, height )


@x = x; @y = y; @width = width; @height = height
end

def union( rect )


... # returns the union of two rectangles
end
end

class Line < Shape


attr_accessor :x1, :y1, :x2, :y2

def initialize( x1, y1, x2, y2 )


@x1 = x1; @y1 = y1; @x2 = x2; @y2 = y2
end
end

class Group < Shape


add( shape ) ... # adds a shape to the group
remove( shape ) ... # removes a shape from the group
each ... # iterates over shapes in the group
end

The visitor pattern defines different operations on shapes and groups as classes with methods that implement the operation for each
type of shape. This involves adding an 'accept' method in each shape class that takes a visitor as an argument and calls back to the
visitor describing the type of shape. (This is the double-dispatch).

class Line
def accept( visitor )
visitor.visit_line( self )
end
end

class Rectangle
def accept( visitor )
visitor.visit_rectangle( self )
end
end

class Group
def accept( visitor )
visitor.visit_group( self )
end
end

Visitor classes can now be defined to implement different operations -- calculate the total area of the shapes, find the bounding
rectangle, draw the shapes, turn the shapes into outlines, etc. For example:

class BoundingRectangleVisitor
attr_reader :bounds

def initialize
@bounds = nil
end

def visit_rectangle( rectangle )


if @bounds
@bounds = @bounds.union( rectangle )
else
@bounds = rectangle
end
end

def visit_line( line )


line_bounds = Rectangle.new( x1, y1, x2-y1, x2-y2 )
if @bounds
@bounds = @bounds.union( line_bounds )
else
@bounds = line_bounds
end
end

# Visit each of the members of the group.


def visit_group( group )
group.each { |shape| shape.accept( self )
end
end

The visitor would be used in this way:

shape = get_shape_from_somewhere
visitor = BoundingRectangleVisitor.new
shape.accept( visitor )
bounding_box = visitor.bounds

The Ruby Way


However, Ruby has another mechanism that can make the Visitor pattern unnecessary -- open class definitions. In Ruby it is
possible to add new features to a class without changing the original definition. Just define new methods within a class statement for
an existing class name. The eagle-eyed reader will have noticed that we already used this mechanism to add the accept methods for
the visitor pattern to the existing shape classes.
For example, to add bounding rectangle calculations to the shapes:

require 'shapes' # assuming we saved the shape classes in shapes.rb

class Rectangle
def bounds
Rectangle.new( x, y, width, height )
end
end

class Line
def bounds
Rectangle.new( x1, y1, x2-y1, x2-y2 )
end
end

class Group
def bounds
rect = nil

each do |s|
if rect
rect = rect.union( s.bounds )
else
rect = s.bounds
end
end
rect
end
end

I think you'll agree, the Ruby way is much simpler.

The Ruby Metaprogramming Way


While it is certainly true the above example will provide an addequate solution in many situations it will have some issues in a large
type hiearchy like a set of nodes generated for a compiler. There are a few problems with the above system:

• Clobbers the namespace of the class for each visitor you define for the nodes
• Provides no method for providing default action without losing the ability to target visitors by inheritance trees
• It's not possible to subclass a visitor class to change a small part of functionality

The following method using Visitable module allows for all of these things, and still seems reasonably rubyish. It exploits the ease
with which one can walk the inheritance tree and define new methods based on events.

#file -- visitor.rb

module Visitable
def accept(visitor,&block)
for klass in self.class.ancestors do
break if (v = visitor.methods.include?("visit_#{klass.name.split(/::/)[-1]}"))
end
if v
visitor.__send__("visit_#{klass.name.split(/::/)[-1]}",self,&block)
else
visitor.default_visit(self,&block)
end
end
def Visitable.included(kls)
kls.module_eval <<-"end_eval"
def self.inherited(child)
child.module_eval "include Visitable"
end
end_eval
end
end

To use, just require the library, and include Visitable in the root nodes of your class hiearchy, and then define some visitors classes
which have a visit_#{Klassname} method for each class you wish to process. If you want a default fall back visitor just define a
default_visit method. To start it just call something like:

root.accept(Visitor.new)

Below is the example test code and output for the Visitable module. Note how you can selectively include and exclude parts of the
inheritance tree. It will also match based on any mixins at the point it was added to the tree.

require 'visitor'

module Children
def initialize(*children)
@children = children
end
def children
@children
end
end

class Root
include Visitable
include Children
end
class Root2
include Visitable
include Children
end
class Root3
include Visitable
include Children
end

module Test
end

class RootChild1 < Root


end
class Root2Child1 < Root2
end
class Root2Child2 < Root2Child1
include Test
end
class Root2Child3 < Root2Child2
end
class Root3Child1 < Root3
end
class Root3Child2 < Root3Child1
end

class Visitor
def initialize()
@name = self.class
end
def visitChildren(klass)
klass.children.each {|child| child.accept(self)}
end
def default_visit(o)
puts "#{@name} - default_visit: #{o.class}"
visitChildren(o)
end
def visit_Root(o)
puts "#{@name} - visit_Root: #{o.class}"
visitChildren(o)
end
def visit_Root2(o)
puts "#{@name} - visit_Root2: #{o.class}"
visitChildren(o)
end
def visit_Test(o)
puts "#{@name} - visit_Test: #{o.class}"
visitChildren(o)
end
def visit_Root3Child1(o)
puts "#{@name} - visit_Root3Child1: #{o.class}"
visitChildren(o)
end
end

class Visitor2 < Visitor


def initialize(name)
@name = "Visitor2<#{name}>"
end
def visit_Root(o)
puts "#{@name} - visit_Root: #{o.class}"
o.children.each {|child| child.accept(Visitor2.new(child.class))}
end
def visit_Root2(o)
puts "#{@name} - visit_Root2: #{o.class}"
o.children.first.accept(self)
o.children.last.accept(Visitor.new)
end
end

class Visitor3
def default_visit(o,&block)
yield o if block_given?
o.children.each {|child| child.accept(self,&block)}
end
end

root = Root.new(
Root2.new(
Root3.new(RootChild1.new,
Root3Child1.new),
Root3Child2.new),
Root2Child2.new(
Root2Child3.new))
puts "# Visitor -- Default Visitor Behavior"
root.accept(Visitor.new)
puts "# Visitor 2 -- Derived Visitor Behavior"
root.accept(Visitor2.new("ROOT"))
puts "# Visitor 3 -- Passing A Block"
root.accept(Visitor3.new) {|root| puts root.class}

Which produces several renditions of a depth first traversal of the graph:

# Visitor -- Default Visitor Behavior


Visitor - visit_Root: Root # found exact type match
Visitor - visit_Root2: Root2 # same
Visitor - default_visit: Root3 # no match, called defaultVisit
Visitor - visit_Root: RootChild1 # no match, found match on parent
Visitor - visit_Root3Child1: Root3Child1 # exact match, despite parent having no match
Visitor - visit_Root3Child1: Root3Child2 # no match, found match on parent
Visitor - visit_Test: Root2Child2 # no match, found match on mixin
Visitor - visit_Test: Root2Child3 # no match, found match on mixin of parent
# Visitor 2 -- Derived Visitor Behavior
Visitor2<ROOT> - visit_Root: Root
Visitor2<Root2> - visit_Root2: Root2
Visitor2<Root2> - default_visit: Root3
Visitor2<Root2> - visit_Root: RootChild1
Visitor2<Root2> - visit_Root3Child1: Root3Child1
Visitor - visit_Root3Child1: Root3Child2
Visitor2<Root2Child2> - visit_Test: Root2Child2
Visitor2<Root2Child2> - visit_Test: Root2Child3
# Visitor 3 -- Passing A Block
Root
Root2
Root3
RootChild1
Root3Child1
Root3Child2
Root2Child2

The second example of the output shows how you can use different visitors to walk other parts of the tree with behavioral
differences. This is very useful for applications such as compilers, where you may find it useful to say walk the parameter list of a
function in order to generate the type declaration. Also note the use of inheritence of visitors to specify different behavior.
The third example shows the ability to pass down a block through the visitor, allowing you to use a proc to apply a method across
the hiearchy. This allows you to make template methods out of some of your visitors.
A neat trick that dovetails with this depends on how you define your initializer for your Visitor. If you define your initializer like this:

def initialize(target = nil,&block)


target.accept(self,&block) if target
end

Then you can visit a hiearchy using

Visitor3.new(root) {|x| ... }

Instead of

root.accept(Visitor3.new) {|x| ... }

All depends on your preference but I find the first method slightly more readable.
Note: there is a bug in that if you use a class named A::B and a class named B there will be a name collision, ie both classes will
match. I'm trying to find an alternate solution to this problem.
-- CharlesComstock

Note: there is a typo in the pickaxe book. Where they mention the VisitorPattern [2], the authors really mean the IteratorPattern.

I've built an abstract visitor class in Smalltalk using code generation.


Lets say you've got a class hierarchy with classes Root, Sub and SubSub. I made root and it's subclasses visitable by sending
visitClassname to the parameter in method visit. Then I generated an AbstractVisitor with method visitRoot as subclass responsibility
(=abstract method), method visitSub sending visitRoot by default and visitSubSub sending visitSub.
Whenever I sublass this AbstractVisitor the only thing to get this NewVisitor work in the first place is to implement
NewVisitor>>visitRoot. Whenever I change the class hierarchy below Root, I just regenerate the AbstractVisitor and most of my
code will still work, no matter how many visitor subclasses I've allready built.

Você também pode gostar