public
Created

Class Variables vs. Class Instance Variables

  • Download Gist
class_vars.rb
Ruby
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
=begin
 
The "Class Variable" Method in class GuidProvider1 (the "@@" notation) is
simpler to use, but violates the principle that the object that owns a
variable is the only one who can directly read or modify it. That is, there
is no privacy or protection, in that there is no way the instances can be
prevented from reading and writing to the variable (for example, the 'next'
instance method does both).
 
In contrast, the "Class Instance Variable" single @ notation as shown in
GuidProvider2 behaves in a way that is consistent with instance variables in
other contexts (that is, within instance methods). The variables are not
readable directly, but instead must be explicitly exposed by providing a
class getter method. In the example, a getter but not a setter is
defined so that instances cannot directly modify the variable.
 
=end
 
 
# "Class Variable" Method
class GuidProvider1
@@highest_seqno = 0
def next
@@highest_seqno += 1
end
def self.highest_seqno
@@highest_seqno
end
end
 
guid_provider = GuidProvider1.new
50.times { guid_provider.next }
puts "Highest sequence number for GuidProvider1 is #{guid_provider.class.highest_seqno}"
 
 
# "Class Instance Variable" Method
class GuidProvider2
@highest_seqno = 0
 
class << self
attr_reader :highest_seqno
end
def self.next
@highest_seqno += 1
end
def next
self.class.next
end
end
 
guid_provider = GuidProvider2.new
5.times { guid_provider.next }
puts "Highest sequence number is #{GuidProvider2.highest_seqno}"
puts "Can't write to it, this will throw an error because we defined a getter but not a setter:"
guid_provider.highest_seqno = 3

Kieth, another thing to be aware of is that @@ variables are not protected; that is, there is only one variable for the entire class heirarchy, and subclasses can freely modify it:

class Foo                                                                          
  @@foo = 1                                                                        
  def foo                                                                          
    @@foo                                                                          
  end                                                                              
end                                                                                

class FooBar < Foo                                                                 
  def next                                                                         
    @@foo += 1                                                                     
  end                                                                              
end                                                                                

FooBar.new.next                                                                    
puts Foo.new.foo  # => 2

This is bad because it ignores any invariants that might be required for the parent class to behave correctly, and errors of this sort can be very hard to track down. @@class_variables have been on the "deprecated, avoid this" list for a long, long time.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.