Thursday, January 26, 2012

Class Variables versus Class Instance Variables in Ruby

I'm going to do a code dump and annotate.

#!/usr/bin/env ruby

class Something
  @@class_variable = 0

  def initialize( name )
    @@class_variable += 1
    @name = name
  end
  def value
    "#{@name},#{@@class_variable}"
  end
end

class SomethingElse < Something
  def initialize( name )
    super(name)
  end
end

joe = Something.new("Joe")
puts "Joe = #{joe.value}"
mary = Something.new("Mary")
puts "Mary = #{mary.value}"
sam = SomethingElse.new("Sam")
puts "Sam = #{sam.value}"
puts "Finished creating. Now Joe = #{joe.value}, Mary = #{mary.value}, and Sam = #{sam.value}"


Class variables are shared among all subclasses.

class SomethingEntirelyDifferent < SomethingElse
  @@class_variable = 0
  def initialize( name )
    super(name)
  end
end

puts
ferdinand = SomethingEntirelyDifferent.new("Ferdinand")
puts "Ferdinand = #{ferdinand.value}"
puts "Created subclass with same classvariable. Now Joe = #{joe.value}, Mary = #{mary.value}, and Sam = #{sam.value}"


Class variables are not shadowed. They are scoped wrt the inheritance chain.
Class variables are candidates for unintentional side-effects.

class SomethingAgain < SomethingElse
  @class_instance_variable = 0

  class << self; attr_accessor :class_instance_variable end

  attr_accessor :instance_variable

  def initialize( name )
    self.class.class_instance_variable += 1
    super(name)
  end
end

albert = SomethingAgain.new("Albert")
puts "Albert = #{albert.value}"
puts "Albert's class = #{albert.class.name} && class instance = #{albert.class.class_instance_variable}"
jane = SomethingAgain.new("Jane")
puts "Created subclass with class instance variable, and two instances."
puts "Jane's class = #{jane.class.name} && class instance = #{jane.class.class_instance_variable}"
puts "Albert's class = #{albert.class.name} && class instance = #{albert.class.class_instance_variable}"


Class instance variables are attached to an object's class object.

class SomethingMore < SomethingAgain
  @class_instance_variable = 1138
  def initialize( name)
    super(name)
  end
end

mike = SomethingMore.new("Mike")
puts "Mike = #{mike.value}"
puts "Mike's class = #{mike.class.name} && class instance = #{mike.class.class_instance_variable}"
puts "Created subclass of class with class instance variable, with its own class instance variable"


Class instance variables aren't visible to subclasses.
Class instance variables are required on subclasses when base-class methods that read or write them.

puts "Albert's class = #{albert.class.name} && class instance = #{albert.class.class_instance_variable}"


Class instance variables are not visible to other subclasses in the inheritance chain.


All in all, the @@class variables encourage collusive coding and appear to carry a high risk of causing race conditions and other unintentional side-effects. The @class instance variables carry a somewhat lesser risk. Barring some obscure trick, a class instance variable is always associated with precisely one class. But even with class instance variables, multiple object instances can gain access to the variable through their own "class" property, with the potential for unintended side-effects.
Post a Comment