Class Instance Variables vs Class Variables

Class Variables in Ruby are unlike class variables in many other programming variables. If you change the value of a class variable, all class variables in the inheritance chain are changed to this new value too. This is explained by Martin Fowler, right here.

The “solution” is to use class instance variables, as is explained here, by John Nunemaker. In his post, John also provides a module to have the class instance value carry over as a kind of constant to a subclass.

My own solution to have inheritable default values for class instance variables is this:

class Polygon
  class << self
    attr_writer :sides
    
    def sides
      @sides || 8
    end    
  end  
end

All subclasses of Polygon will share the default value of 8 sides, but each subclass can also easily be given its own number of sides.

class Triangle < Polygon
  @sides = 3
end

class Octogon < Polygon; end

This does what you'd expect:

>> Polygon.sides
=> 8
>> Triangle.sides
=> 3
>> Octogon.sides
=> 8

The new value for class Triangle is not carried down the inheritance chain, though. Let's say you have a class FantasyShape:

class FantasyShape < Triangle; end

What happens now is this:

>> Triangle.sides
=> 3
>> FantasyShape.sides
=> 8

To fix this, you'd have to override the class method sides for FantasyShape:

class FantasyShape < Triangle
  def self.sides
    @sides || superclass.sides
  end    
end

Or better yet, start the superclass lookup in the top of the inheritance chain:

class Polygon
  class << self
    attr_writer :sides
    
    def sides
      @sides || ((superclass.respond_to?('sides')) ? superclass.sides : 8)
    end    
  end  
end

Now we can simplify the code for class FantasyShape back to:

class FantasyShape < Triangle; end

And voilà:

>> Triangle.sides
=> 3
>> FantasyShape.sides
=> 3

Ruby rocks!


Personal Tools