views:

122

answers:

2

how can i extend the singleton pattern to a number of objects for a class i.e. how can i have only 5 objects of a class at max and not more than that in ruby

+1  A: 

Multiton with size constraints.

Here is a naive implementation of a Multiton module that you can include in any class. The default number of objects it can create is 5, and you can override this limit by defining a class method called max_instances that returns a number (maximum instances allowed) in your class.

module Multiton

  MAX_INSTANCES = 5
  COUNT_HOOK = :max_instances

  module MultitonClassMethods
    def instance
      size = @instances.size
      max = respond_to?(COUNT_HOOK) ? self.send(COUNT_HOOK) : MAX_INSTANCES
      @instances << new if size < max
      @instances[rand(size)]
    end
  end

  def self.included(klass)
    klass.class_eval {
      @instances = []
    }
    klass.private_class_method :new
    klass.extend(MultitonClassMethods)
  end

end

Include the module in a class to make it a multiton.

# Falls back to Multiton::MAX_INSTANCES 
class Person
  include Multiton
end

# Overrides the number of allowed instances in max_instances
class Resource
  include Multiton

  def self.max_instances
    58
  end
end

Since the objects are returned randomly from a pool in this multiton, you may not get all the objects back over a short run. But as more objects are requested, it should even out. You can change this behavior in the Multitonmodule by cycling through objects instead of randomly picking one.

people = []
1000.times do
  people << Person.instance
end
# should print 5, but may print a smaller number
p people.uniq.size

resources = []
1000.times do
  resources << Resource.instance
end
# should print 58, but may print a smaller number
p resources.uniq.size
Anurag
You are just repeating his question.
johannes
thanks for showing me the error of my ways johannes. i was just trying to rake in some free points :P. let me add more to the answer now
Anurag
just one doubt will this be thread safe?
Hitesh Manchanda
nope. this is not thread safe. you might find this helpful for that - http://raa.ruby-lang.org/project/multiton/
Anurag
+3  A: 

Example code:

# Example class which can be instanciated at most five times
# Naive approach with Class variable

class FiveAtMost
  @@instances = 0

  def initialize()
    if @@instances >= 5
      raise "No more than five instances allowed."
    else
      @@instances += 1
    end
    p "Initialized instance #{@@instances}"
  end
end

one = FiveAtMost.new
two = FiveAtMost.new
three = FiveAtMost.new
four = FiveAtMost.new
five = FiveAtMost.new

# will raise RuntimeError: No more than five instances allowed.
six = FiveAtMost.new

Since the moment when an object gets garbage collected is not predictable, you'll need some kind of workaround for the that event. Maybe you find this useful: http://pleac.sourceforge.net/pleac_ruby/classesetc.html#AEN710

The MYYN
I guess keeping the created instances in an array (in class variable, which could be used instead of `@@instances` for instantiation limiting as well) would prevent garbage collecting them?
Mladen Jablanović
Yes, I called my approach naive. With your suggestion and some custom defined finalizer, it may be a proper solution. If you like (and can), edit my post, since I'm short on time right now.
The MYYN