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
views:
122answers:
2Multiton 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 Multiton
module 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
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