Singleton Pattern Revisited


Java Geeks know howto implement Singletons in Java. There are few rules to be considered:

  • define private constructor
  • add a static method (getInstance) which has full control about the creation of the single object
  • add the final modifier to the class to avoid defining subclasses

Paradoxically, in Ruby the singletons can be implemented by “inheritance” even though by mixing a module named … (right) … Singleton!

In Java you have to know what to do to form the GOF patterns in common. In Ruby the patterns are supported by Ruby API (at least this pattern but there are I’sure there are more). This is because of the meta nature of Ruby!

Look at this example:

require 'singleton'

class ReportManager
  include Singleton

  def print
    @name = self.class.to_s
    puts ".. #{@name} is printing PDF ..."
  end
end

mng0 = ReportManager.instance
mng0.print

Output:

.. ReportManager is printing PDF ...

Now, ReportManager is supposed to be used as singleton by calling the class method “instance”. The second attempt to instantiate ReportManager returns the existing instance, so that the comparison of both will issue true.

mng1 = ReportManager.instance
mng1.print

puts mng0 == mng1

In Java, it’s common practice to define the Singleton class as final to prevent extending it (some people insist that this behavior is rather an anti-pattern. Read here for more information: http://tech.puredanger.com/2007/07/03/pattern-hate-singleton/).

Consider this example and you’ll see that extending the singleton class is possible. Calling instance method on the subclass will return a new instance of SpecialReportManager which is also ReportManager instance as well.

class SpecialReportManager < ReportManager

  def print
    super
    puts ".... #{@name} is adding company brand..."
  end

end

mng2 = SpecialReportManager.instance
mng2.print

mng3 = SpecialReportManager.instance
mng3.print

Output:

.. SpecialReportManager is printing PDF ...
.... SpecialReportManager is adding company brand...
.. SpecialReportManager is printing PDF ...
.... SpecialReportManager is adding company brand...

Accordingly, this code reveals that mng0 and mng2 are different.

puts mng0 == mng1   #true
puts mng2 == mng3   #true
puts mng0 == mng2   #false

Performing object space lookup will confirm that:

ObjectSpace.each_object(ReportManager) do |o|
  puts o.inspect
end

Output:

#<SpecialReportManager:0x00000002626950 @name="SpecialReportManager">
#<ReportManager:0x00000002626a68 @name="ReportManager">

I’m not sure whether this behavior is intended or not (sorry, I just started learning Ruby!) but if you want to prevent any inheritance attempts add this callback to your class (I think in some cases like implementing privilege manager etc. this might make sense):

class ReportManager
  include Singleton

  def self.inherited(subclass)
    raise "not allowed to inherit!"
  end
  
  def print
    @name = self.class.to_s
    puts ".. #{@name} is printing PDF ..."
  end
end
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s