views:

368

answers:

3

I can understand why you would need a class variable to keep track of things like the total number of objects that have been instantiated in that class.

And I can understand why you would need an instance variable to store attributes of a particular object in that class.

But class instance variables I just can't seem to justify.

As I understand it, they're just like class variables except that they are not visible to subclasses the way class variables are.

It seems that the uses for this would be quite limited. Or am I wrong? Has anyone found a good use for class instance variables in their code? Or could you give an example of a situation where this type of nuance would be valuable?

+2  A: 

Class variables are shared across all instances of a class, which includes all subclasses. Sometimes this variable across a hierarchy is exactly what is needed, but sometimes you might prefer a different variable for each class. Unlike class variables, class instance variables will take different values for each class object.

http://martinfowler.com/bliki/ClassInstanceVariable.html

p01nd3xt3r
+4  A: 

Say you want to count the number of instances of a class (not including subclasses)

class A
  @count = 0
  @@count = 0
  def self.inherited(subclass)
    subclass.instance_eval { @count = 0 }
  end
  def self.num_instances
    @count
  end
  def self.num_subclass_instances
    @@count
  end
  def self.new
    @count += 1
    @@count += 1
    super
  end
end
class B < A
end
class C < B
end

A.new
B.new
A.new
B.new
B.new
C.new

A.num_instances #=> 2
A.num_subclass_instances #=> 6
B.num_instances #=> 3
B.num_subclass_instances #=> 6
C.num_instances #=> 1
C.num_subclass_instances #=> 6

You can't use the class variable, since that's shared among all classes and its subclasses. Note how the changes made to @@count by B and C are reflected in A, but @count is not shared.

In general, though, it can be very useful for storing any class-specific settings. _why uses it in Dwemthy's Array to specify initial values for instance attributes, and it comes up a lot whenever doing ruby metaprogramming.

rampion
Thanks. Great example(s). So, basically, if we only had class variables, we wouldn't be able to do things like these localized counts for A, B and C - we would be stuck with the equivalent of your num_subclass_instances. I think I get it now. The only part of your code I didn't understand was: def self.inherited(subclass) subclass.instance_eval { @count = 0 } end. What does that do?
pez_dispenser
So I initialized A's @count in the definition of A, but I needed to initialize @count to 0 for all subclasses of A as well, which is what that code does. When A is inherited by `subclass`, we open up subclass and initialize its @count to 0. Try removing that, and you'll see NoMethodErrors when B and C are instantiated, since Ruby won't know how to add 1 to nil (the default value of @count if not instantiated)
rampion
pez_dispenser
I didn't know that. I suppose in that case you could use subclass.instance_variable_set('@count',0) http://www.ruby-doc.org/core-1.9/classes/Object.html#M000306
rampion
ok .
pez_dispenser
+3  A: 

Yes, actually I have. This is only slightly modified and trimmed from what I had:

class Widget
  # class instance variable pattern
  class << self; attr_accessor :color; end

  def show_color()
    "This widget is #{self.class.color}"
  end
end
class WidgetR < Widget
  @color = "Russet"
end
class WidgetC < Widget
  @color = "Cordovan"
end
class WidgetL < Widget
  @color = "Liver"
end

WidgetR.new.show_color #=> "This widget is Russet"

But I'm not sure it is really necessary in the case I used it. I could have just overridden the method. Or provided a color method. Or stored it in a class variable as a Hash. Or even kept a copy in each instance (ok, that one is kinda yech). I'm sure there are other possibilities...

There are a variety of alternatives and the syntax is awkward. Given that I'm guessing the instances where it is the most natural thing to use are probably fairly rare.

It might help you to try to reproduce this behavior with class and instance variables and see that it is difficult to achieve (although it is easy if you define methods, etc).

C.J.

CJ