Sony Santos
Sony Santos

Reputation: 5545

Best way to maintain a collection of created objects

I want to maintain a collection of created objects of a Klass. I have two methods im mind:

First: Thru initialize:

class Klass
  @@objs = []

  def initialize *args
    # ... do regular initialization ...
    Klass.send :add, self
  end

  class << self
    def objs
      @@objs
    end

    private

    def add obj
      @@objs << obj
    end
  end
end

Second: Thru new:

class Klass
  @@objs = []

  class << self
    def objs
      @@objs
    end

    alias_method :old_new, :new    # never thought someday I'd name something as "old_new"!
    private :old_new

    def new *args
      obj = super
      @@objs << obj
      obj
    end
  end
end

Testing:

a = Klass.new
b = Klass.new
puts a.inspect            #=> #<Klass:0xb7786ba0>
puts b.inspect            #=> #<Klass:0xb7786b78>
puts Klass.objs.inspect   #=> [#<Klass:0xb7786ba0>, #<Klass:0xb7786b78>]

Both works, and I tend to prefer the second way, because of "add": it must be private, and I must use "Klass.send". What is the best way? Is there some other (better) manner?

Upvotes: 2

Views: 76

Answers (4)

Andrew Grimm
Andrew Grimm

Reputation: 81530

You could also do

class IndividualKlass
end

class KlassCreator
  def initialize(individual_klass)
    @individual_klass = individual_klass
    @objects = []
  end

  def create_object
    object = @individual_klass.new
    @objects << object
    object
  end
end

klass_creator = KlassCreator.new(IndividualKlass)
IndividualKlass = nil # Or remove_const or something

object = klass_creator.create_object

Upvotes: 0

Phrogz
Phrogz

Reputation: 303261

Note that your alias_method and private were not needed, as the new you normally call is Class#new. Also, there's no reason to use a class variable (unless you are trying to track sub-class creations on the master class). This works just as well, if not better:

class Foo
  class << self; attr_reader :objs; end
  def self.new(*a,&b)
    super.tap{|o| (@objs||=[]) << o}
  end
end
class Bar < Foo; end
2.times{ Foo.new }
3.times{ Bar.new }

p Foo.objs
#=> [#<Foo:0x2bb6260>, #<Foo:0x2ba2400>]

p Bar.objs
#=> [#<Bar:0x2ce6000>, #<Bar:0x2baf4e0>, #<Bar:0x2b94f40>]

The benefit of this technique over appending to the array in your initializer is that you don't need to remember to call super in the initialize of your subclasses. Indeed, you don't need to even define an initialize in your subclasses.

Upvotes: 1

Mike Lewis
Mike Lewis

Reputation: 64147

Sine initialize is called every time a new object is created, I would take advantage of this fact and just do:

def initialize *args
    # ... do regular initialization ...
    @@objs << self
end

Upvotes: 3

Jakub Hampl
Jakub Hampl

Reputation: 40543

Couldn't you simply just << in the normal constructor? Simple code and you don't have to redefine new which is rarely a good idea.

Upvotes: 1

Related Questions