Patterns in Ruby: Singleton Pattern
The Singleton pattern is the black sheep of the pattern family. It was easy to grasp, developers everywhere applied it liberally, and an inevitable backlash came against its overuse.
I won't make any judgments or reccomendations on when to use it - but I will show you just how easy it is to apply in Ruby.
The literal translation of the pattern is to create a class level instance method and to hide the new method.
class Example
def initialize
# do something?
end
def self.instance
return @@instance if defined? @@instance
@@instance = new
end
private_class_method :new
end
puts Example.instance.object_id #=> 21783380
puts Example.instance.object_id #=> 21783380This example gives you the basic idea, but it doesn't cover many cases you'd like to handle, like cloning or duping the singleton. It also doesn't hide the class level allocate method, which means a sneaky coder could still create another instance through some hacking.
Lastly, it's not thread safe.
Luckily, Ruby already provides a module for making classes singletons. It's in the standard library, inside 'singleton.rb'. Here's how you use it:
require 'singleton'
class Example
include Singleton
endThis module will do the same thing as my example above but will also handle hiding allocate, overriding the clone and dup methods, and is thread safe. The library file itself contains a bunch of examples of its usage, and those interested should definitely read through it.
One thing to note about these implementations is that the instance method takes no arguments, so none are passed on to the object's constructor. This makes sense because the first time instance is called those will be the arguments used for this global instance. Setters are typically more appropriate for most singletons.
Since singletons are global in nature setters should be at the class level. As an extra bonus here's the implementation of the class level attr_ methods to generate the vanilla getter/setter methods (stolen from Rails).
class Class # :nodoc:
def cattr_reader(*syms)
syms.flatten.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}
@@#{sym}
end
def #{sym}
@@#{sym}
end
EOS
end
end
def cattr_writer(*syms)
syms.flatten.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}=(obj)
@@#{sym} = obj
end
def #{sym}=(obj)
@@#{sym} = obj
end
EOS
end
end
def cattr_accessor(*syms)
cattr_reader(*syms)
cattr_writer(*syms)
end
endNow we can create a more realistic singleton:
require 'singleton'
class JimmyGrimble
include Singleton
cattr_reader :boots
cattr_accessor :football
endAbout this entry
You’re currently reading “Patterns in Ruby: Singleton Pattern,” an entry on Late to the Party by Chris
- Published:
- 2pm on 10/31/06
- Categories:
- Programming, Ruby
- Tags:
- design, patterns, programming, ruby, singleton
No Comments
Jump to comment form | comments rss | trackback uri