/  32
 
Examples of design patterns implemented in Ruby, a topic suggested byHughSasse.
"Each pattern describes a problem which occurs over and over again in our environment, and thendescribes the core of the solution to that problem, in such a way that you can use this solution a milliontimes 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 trueway" to solve a particular problem. Often two patterns will describe opposite solutions to a problem; the most suitable choicedepends on the context in which the problem occurs. Similarly, there is no one true way to implement a pattern; the pages belowoffer examples not code templates. In practice, the design of single class will involve the use of multiple documented patterns andsome project specific patterns. The design and implementation of the class will therefore combine the roles of the patterns itparticipates in, rather than be, for example, "a composite" or "a visitor".
Patterns
 
CompositePattern? 
VisitorPatternAlmost all of these examples are correct Ruby programs. If example output is shown, you should be able to copy code from the webpage, run it in a Ruby interpreter and get the documented output. If you cannot run the examples, please letNatPryceknow.
"
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 andthey can be used polymorphically; code calling the
new
method of a class object does not need to know the exact type of object thatthe class creates.
 
class Foo; endclass Bar; end# Here is the use of the Abstract Factory patterndef create_something( factory )new_object = factory.newputs "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:
 
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 factorycan yield to the block, or call the proc, every time a new object is needed.
def create_something_with_blocknew_object = yieldputs "created a #{new_object.class} with a block"enddef create_something_with_proc( &proc )new_object = proc.callputs "created a #{new_object.class} with a proc"endcreate_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 shieldthe 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 MazeGamedef create_maze(factory)maze = factory.new_mazeroom1 = factory.new_room 1room2 = factory.new_room 2door = factory.new_door room1, room2maze.add room1maze.add room2room1.north= factory.new_wallroom1.east= doorroom1.south= factory.new_wallroom1.west= factory.new_wallroom2.north= factory.new_wallroom2.east= factory.new_wallroom2.south= factory.new_wallroom1.west= doorreturn mazeendend
Obviously, the new_xxx are simple to think as Xxx.new, as pointed above in this page, and the instance of a Factory can justbecome a class or module holding the various Xxx:
module DwemthyDungeonclass Maze...endend
 
class MazeGamedef 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 MazeGamedef create_mazemaze = Factory::Maze.newendMazeGame::Factory=DwemthyDungeon
But then, again, The whole Factory thing becomes useless once we have a module:
class MazeGamedef create_mazemaze = Maze.newend...endMazeGame.send :include, DwemthyDungeon
As someone said, often patterns are invisible in ruby :) --GabrieleRenzi? If you want more information, there are some good examples of theAbstractFactoryPatternathttp://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. Aservice object, rather than providing a client with a handle to be passed as an argument to the operations of its abstract interfaceinstead 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 interfacethrough 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 stilla very useful pattern when objects need to maintain different state for each of their callers. Here is a simplistic example:
class Serverdef initialize@client_id = 0end def session@client_id += 1Session.new( self, @client_id )end def service_client( session )puts "servicing client #{session.client_id}"endendclass Sessionattr :client_id def initialize( server, client_id )@server = server@client_id = client_idend 

Share & Embed

More from this user

Add a Comment

Characters: ...