Anda di halaman 1dari 32

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

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.

"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:

class MazeGame def create_maze (factory) maze = factory::Maze.new 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

"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

"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.

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

"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

"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! shake!
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__(

end

end

name, *args )

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!
in
out
in
out
shake it all about
shake!

"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....

"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 =

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"]
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'"

"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

"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 For example:

...

in' statements.

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]
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

"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
4
14
120

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

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?

"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
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

end

}

end

CEEND

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

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

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

"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
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

"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
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
55

Distributed object invocation in 59 lines of code!

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.

socket.close end end acc = Accumulator.new server = TCPServer.new( '127.0.0.1', 54321 ) puts "waiting for connectionsRepositoryPattern 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. " id="pdf-obj-20-22" src="pdf-obj-20-22.jpg">

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

end

end

return true

#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.
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

"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
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

"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

"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
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:

"" 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"
"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
10
24

"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 ...

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:

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
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
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 Visitor - visit_Root2: Root2 Visitor - default_visit: Root3 Visitor - visit_Root: RootChild1

# found exact type match # same # no match, called defaultVisit # no match, found match on parent

Visitor - visit_Root3Child1: Root3Child1 Visitor - visit_Root3Child1: Root3Child2

# exact match, despite parent having no match # no match, found match on parent

Visitor - visit_Test: Root2Child2 Visitor - visit_Test: Root2Child3 # Visitor 2 -- Derived Visitor Behavior Visitor2<ROOT> - visit_Root: Root Visitor2<Root2> - visit_Root2: Root2 Visitor2<Root2> - default_visit: Root3

# no match, found match on mixin # no match, found match on mixin of parent

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 --
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.

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.