Why Should @@Class_Variables Be Avoided in Ruby

Why should @@class_variables be avoided in Ruby?

Class variables are often maligned because of their sometimes confusing behavior regarding inheritance:

class Foo
@@foo = 42

def self.foo
@@foo
end
end

class Bar < Foo
@@foo = 23
end

Foo.foo #=> 23
Bar.foo #=> 23

If you use class instance variables instead, you get:

class Foo
@foo = 42

def self.foo
@foo
end
end

class Bar < Foo
@foo = 23
end

Foo.foo #=> 42
Bar.foo #=> 23

This is often more useful.

Why should we avoid using class variables @@ in rails?

Simply because they are not thread safe. Many rails=capable servers are multi-threaded. That means there may be multiple running instances of your application at any given time and any request by one of your users is going to be arbitrarily assigned to one of them. Class variables are not shared between processes so there is a possibility that your class variable will be different in a subsequent request.

Even if you deliberately manage to run your app in a single threaded server, there is no guarantee that your app won't be restarted between requests, losing your class variable.

If you want functionality similar to what class variables provide, I strongly recommend that you look into key-value stores such as Memcached or Redis.

How can I avoid using class variables in Ruby

@@cost behaves more like a constant (i.e. it won't change during runtime), so you should use one instead:

COST = 0.0946

@@kwh should be an instance variable, since it is used only within the instantiated object, so you could use @kwh instead:

@kwh = (watt / 1000) * hours

And daily_cost = @@kwh * @@cost will become:

daily_cost = @kwh * COST

That will avoid the use of class variables, but you could also eliminate @kwh altogether since you don't use it anywhere else.

So, instead of:

def watt_to_kwh(hours)
@kwh = (watt / 1000) * hours
end

You could just do:

def watt_to_kwh(hours)
(watt / 1000) * hours
end

And use it like this in cost_of_energy method:

def cost_of_energy
puts "How many hours do you use the #{self.name} daily?"
hours = gets.chomp.to_i
daily_cost = watt_to_kwh(hours) * COST
montly_cost = daily_cost * 30
puts "Dayly cost: #{daily_cost}€"
puts "montly_cost: #{montly_cost}€"
end

Why is using a class variable in Ruby considered a 'code smell'?

As you can find in their documentation on Class Variables:

Class variables form part of the global runtime state, and as such make it easy for one part of the system to accidentally or inadvertently depend on another part of the system. So the system becomes more prone to problems where changing something over here breaks something over there. In particular, class variables can make it hard to set up tests (because the context of the test includes all global state).

Essentially, it's a manifestation of global state, which is almost universally considered evil, because it makes tests more difficult and results in a much more fragile class/program structure.

This Stack Overflow question may also be worth reading, which shows the main problem with class variables: if any class inherits from your class and modifies the class variable, every instance of that variable changes, even from the parent! This understandably gives you a way to shoot yourself in the foot easily, so it may be best to avoid them unless you're very careful.

It's also worth comparing class variables with class instance variables. This question has a few good examples which illustrate the usage differences, but in essence class variables are shared, whereas class instance variables are not shared. Therefore, to avoid unwanted side effects, class instance variables are almost always what you want.

How to avoid class and global variables

You misunderstand the term "class instance variable". It means "instance variable on a Class object", not "instance variable on an instance of some class".

  class Person
attr_accessor :memories # instance variable, not shared

class << self
attr_accessor :memories # class instance variable, shared between
# all instances of this class
end
end

Obviously, sometimes you do need to use class instance variables. Refrain from using class variables (@@memories) as they are shared between all classes in the hierarchy (the class and its children), which may lead to surprising behaviour.

Should I use class variables or class-instance variables for class static variables in Ruby?

I recently discovered ActiveSupport defines class_inheritable_accessor, which does what the class-instance variables do with the advantage that objects are not shared across inheritance, and you can have a default value for the variable when subclassing.

class Foo
class_inheritable_accessor :x, :y
end

Foo.x = 1

class Bar < Foo
end

Bar.x #=> 1
Bar.x = 3
Bar.x #=> 3
Foo.x #=> 1

More info here

Just for completeness: of the two presented options, I prefer going with the class-instance variables, since is often the expected behavior.

Ruby class instance variable vs. class variable

Instance variable on a class:

class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end

class Child < Parent
@things = []
end

Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new

p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]

Class variable:

class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end

class Child < Parent
end

Parent.things << :car
Child.things << :doll

p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]

mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new

[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]

With an instance variable on a class (not on an instance of that class) you can store something common to that class without having sub-classes automatically also get them (and vice-versa). With class variables, you have the convenience of not having to write self.class from an instance object, and (when desirable) you also get automatic sharing throughout the class hierarchy.


Merging these together into a single example that also covers instance variables on instances:

class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class

def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end

attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end

class Child < Parent
@shared_things = []
end

And then in action:

mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new

Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks

p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]

p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]

p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]


Related Topics



Leave a reply



Submit