xml version="1.0" encoding="utf-8"?> Instance variables, class variables, and inheritance in Ruby

Sporkmonger

purveyor of fabulously ambiguous eating utensils

Instance variables, class variables, and inheritance in Ruby

Posted by sporkmonger
Written February 18th, 2007

I’m seeing a lot of code that indicates to me that people don’t have a complete grasp of when to use class variables in Ruby.

Here’s a quick example script that should give people a better idea of what’s going on:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

class ExampleClass
  @variable = "foo"
  @@variable = "bar"
  
  def initialize
    @variable = "baz"
  end
  
  def self.test
    puts @variable
  end
  
  def test
    self.class.test
    puts @@variable
    puts @variable
  end
end

class ExampleSubclass < ExampleClass
  @variable = "1"
  @@variable = "2"
  
  def initialize
    @variable = "3"
  end
end

first_example = ExampleClass.new
first_example.test

puts "---"

second_example = ExampleSubclass.new
second_example.test

Output:

foo
2
baz
---
1
2
3

Essentially, when you declare a class variable in a base class, it’s shared with all subclasses. Changing its value in a subclass will affect the base class and all of its subclasses all the way down the inheritance tree. This behavior is often exactly what’s desired. But equally often, this behavior is not what was intended by the programmer, and it leads to bugs, especially if the programmer did not originally expect for the class to be subclassed by someone else.

I strongly encourage Ruby developers to sit down and think about how they want their classes to behave if subclassed by someone else. Be careful about using class variables, because often what you actually wanted was an instance variable on the class object.

Tags:
  1. Vrensk Vrensk :
    Written February 20th, 2007 at 07:22 PM

    Great demonstration, and really handy to have sort of a minimal test case for the variables mess.

    A thought though: since Ruby doesn’t have constructor chaining, wouldn’t there be a point in adding a call to “super” from initialize in the subclass?

  2. Written February 21st, 2007 at 03:11 PM

    Vrensk:

    You could. But I didn’t because I didn’t want to distract from the point I was making.

  3. Reda Reda :
    Written March 29th, 2007 at 03:19 PM

    Hey Bob,

    This article sounds a lot like you’re encouraging rather than discouraging strict OOP. Not what I heard from you last time we spoke..

    Don’t bother constructing an elaborate counter-argument… I’m too ignorant to understand what’s going on on this site anyway, and probably wouldn’t understand what you’re talking about . :-)

    How’s it going?

  4. Written March 29th, 2007 at 03:25 PM

    Hey Reda!

    What’ve you been up to lately?

    Regarding OOP, well, technically, this post isn’t advocating anything, really. Just an explanation of how Ruby treats instance and class variables. Nothing more. But I’m still of the opinion that people misuse objects and pollute their objects with methods that should have been defined more globally on a class or module instead. Is that advocating against the use of OOP? No, not really. More like pointing out that you can have too much of a good thing and end up with a confusing API.

Leave a Response

NOTE: I'm afraid Javascript needs to be on in order to comment.

Comments should be formatted using Textile.

Ruby code should be enclosed within a <macro:code lang="ruby"> element. Other languages are supported. For output you can simply omit the lang attribute.