Design Patterns in Ruby

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:

Transcript of Design Patterns in Ruby

Page 1: Design Patterns in Ruby

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; endclass Bar; end

# Here is the use of the Abstract Factory patterndef create_something( factory ) new_object = factory.new puts "created a new #{new_object.class} with a factory"end

# Here we select a factory to usecreate_something( Foo )create_something( Bar )

Running the code above results in the output:

Page 2: Design Patterns in Ruby

created a Foo with a factorycreated 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 blockcreated a Bar with a blockcreated a Foo with a proccreated 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 endend

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

Page 3: Design Patterns in Ruby

class MazeGame def create_maze (factory) maze = factory::Maze.newend

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

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

class Session attr :client_id def initialize( server, client_id ) @server = server @client_id = client_id end

Page 4: Design Patterns in Ruby

def service @server.service_client( self ) endend

server = Server.newclient1 = server.sessionclient2 = server.session

client1.serviceclient2.service

Running this program results in the output:

servicing client 1servicing 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 endend

class RoundPeg attr_reader :radius def initialize( radius ) @radius = radius endend

class RoundHole attr_reader :radius

def initialize( r ) @radius = r end def peg_fits?( peg ) peg.radius <= radius endend

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

Page 5: Design Patterns in Ruby

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

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 ) endend

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

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.

Page 6: Design Patterns in Ruby

# x_str and y_str are strings that the user has inputdef 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 / yend

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 == 0end

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 endend

class CongaLeader def shimmy puts "shimmy!" end def shake puts "shake!" endend

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

This program produces the output:

shimmy

Page 7: Design Patterns in Ruby

shimmyshimmy!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 ) endend

class HokieCokieDancer < CongaDancer def in puts "in" end def out puts "out" end def shake puts "shake it all about" next_in_line.shake endend

line = CongaDancer.new( HokieCokieDancer.new ( CongaLeader.new ) )line.inline.outline.inline.outline.shake

This produces the output:

inoutinoutshake it all aboutshake!

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:

Page 8: Design Patterns in Ruby

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 0did 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 endend

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 0did all commands, count is 55undid all commands, count is 0redid 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

Page 9: Design Patterns in Ruby

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

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

Page 10: Design Patterns in Ruby

endend

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 endend

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

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

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 - foo84006168 - 'foo'84006168 - "'foo'"

JasonArhart

Page 11: Design Patterns in Ruby

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 ) endend

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

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

Page 12: Design Patterns in Ruby

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 ) endend

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.01OK (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 listprint_elements hash

Results in the output:

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

Page 13: Design Patterns in Ruby

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 endend

class NullTree def size 0 end

def sum_of_values 0 end

def product_of_values 1 endend

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

p tree.sizep tree.sum_of_valuesp tree.product_of_values

Results in the output:

414120

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

require 'singleton' class NullTree include Singleton # ... end

Page 14: Design Patterns in Ruby

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.

Page 15: Design Patterns in Ruby

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 endend

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 endend

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

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

counter.inccounter.inccounter.deccounter.dec

Running this program produces the output:

counter now has value 1counter now has value 2counter now has value 1counter 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.

Page 16: Design Patterns in Ruby

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 endend

- 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 } endend

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

class TextBox

Page 17: Design Patterns in Ruby

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 endend

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::newtext_changed_id=box.on_text_changed { puts "Text changed!" } # registering a callback5.times{ box.on_key_pressed { |k| puts "Key pressed: #{k}" }}box.txt= "New text!"box.del_text_changed( text_changed_id) # unregistering the callbackbox.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?

Page 18: Design Patterns in Ruby

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 PaperOccurs: Saturday

Event: Postman comesOccurs: Weekdays

Event: BarbecueOccurs: 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 endend

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 endend

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

Page 19: Design Patterns in Ruby

end

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

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 endend

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)) endend

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.

Page 20: Design Patterns in Ruby

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 endend

For good measure, here is the server program:

require 'socket' class Accumulator def accumulate( *args ) total = 0 args.each { |a| total = total + a } total endend

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

Page 21: Design Patterns in Ruby

socket.close endend acc = Accumulator.newserver = 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.

Page 22: Design Patterns in Ruby

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 endend

#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) endend

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

#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

Page 23: Design Patterns in Ruby

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 endend

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

songTest = SongTest.newsongTest.test_song_and_artistsongTest.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.newdef MyObject.foo; ...; enddef MyObject.bar; ...; end

or:

MyObject = Object.newclass << MyObject def foo; ...; end def bar; ...; end

Page 24: Design Patterns in Ruby

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,:zweiend

is just an abbreviation for

Mod = Module.newclass << Mod def eins;end def zwei;endendmodule Mod private def eins;end def zwei;endend

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 endprivate class Context def initialize @state = Offline.new(self) end attr_accessor :state endend

class ClientState

Page 25: Design Patterns in Ruby

def initialize(context) @context = context inform endend

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

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

client = Client.newclient.send_message("Hello")client.connectclient.send_message("Hello")client.connectclient.receive_messageclient.disconnect

Running the code above results in the output:

offlineerror: not connectedconnected"Hello" senterror: already connectedmessage receivedoffline

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

Page 26: Design Patterns in Ruby

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 endend

class Sum < Accumulator def initial_value 0 end def accumulate_value( total, v ) total + v endend

class Product < Accumulator def initial_value 1 end def accumulate_value( total, v ) total * v endend

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

Results in the output:

1024

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

Page 27: Design Patterns in Ruby

"" 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 ) } totalend

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:

1024

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 Shapeend

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 endend

class Line < Shape attr_accessor :x1, :y1, :x2, :y2 def initialize( x1, y1, x2, y2 ) @x1 = x1; @y1 = y1; @x2 = x2; @y2 = y2 endend

class Group < Shape

Page 28: Design Patterns in Ruby

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

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 ) endend

class Rectangle def accept( visitor ) visitor.visit_rectangle( self ) endend class Group def accept( visitor ) visitor.visit_group( self ) endend

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 ) endend

The visitor would be used in this way:

shape = get_shape_from_somewherevisitor = BoundingRectangleVisitor.newshape.accept( visitor )bounding_box = visitor.bounds

The Ruby Way

Page 29: Design Patterns in Ruby

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 ) endend

class Line def bounds Rectangle.new( x1, y1, x2-y1, x2-y2 ) endend class Group def bounds rect = nil each do |s| if rect rect = rect.union( s.bounds ) else rect = s.bounds end end rect endend

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

Page 30: Design Patterns in Ruby

endend

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 endend

class Root include Visitable include Childrenendclass Root2 include Visitable include Childrenendclass Root3 include Visitable include Childrenend

module Testend

class RootChild1 < Rootendclass Root2Child1 < Root2endclass Root2Child2 < Root2Child1 include Testendclass Root2Child3 < Root2Child2endclass Root3Child1 < Root3endclass Root3Child2 < Root3Child1end

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

Page 31: Design Patterns in Ruby

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) endend

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) endend

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

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 BehaviorVisitor - visit_Root: Root # found exact type matchVisitor - visit_Root2: Root2 # sameVisitor - default_visit: Root3 # no match, called defaultVisitVisitor - visit_Root: RootChild1 # no match, found match on parentVisitor - visit_Root3Child1: Root3Child1 # exact match, despite parent having no matchVisitor - visit_Root3Child1: Root3Child2 # no match, found match on parent Visitor - visit_Test: Root2Child2 # no match, found match on mixinVisitor - visit_Test: Root2Child3 # no match, found match on mixin of parent# Visitor 2 -- Derived Visitor BehaviorVisitor2<ROOT> - visit_Root: Root Visitor2<Root2> - visit_Root2: Root2 Visitor2<Root2> - default_visit: Root3Visitor2<Root2> - visit_Root: RootChild1Visitor2<Root2> - visit_Root3Child1: Root3Child1

Page 32: Design Patterns in Ruby

Visitor - visit_Root3Child1: Root3Child2Visitor2<Root2Child2> - visit_Test: Root2Child2Visitor2<Root2Child2> - visit_Test: Root2Child3# Visitor 3 -- Passing A BlockRootRoot2Root3RootChild1Root3Child1Root3Child2Root2Child2

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.