is_a? is almost always not the best solution.
Let’s say that you own a car dealership and you have standard markup rates for all vehicles previously of
Consider the following models representing this requirement.
class Vehicle < ActiveRecord::Base def sale_price price * markup_rate end def markup_rate 0.2 end end class Car < Vehicle end class Suv < Vehicle end
Your requirements may be stated like this:
If the Vehicle is a SUV we want to charge a markup rate of 40%. The markup rate should remain the same for other vehicles
Solution 1: Using is_a? (or alias kind_of?)
class Vehicle < ActiveRecord::Base # Code omitted from base example to focus on the change def markup_rate self.is_a?(Suv) ? 0.4 : 0.2 end end
Here we changed the
markup_rate for the vehicle. This may be because Ruby allows us to be expressive.
Some downsides to this approach include:
- Conditional logic is a bit complicated and should be avoided where necessary
- If we added another
markup_rate, the conditional expression would have several branches
- It is not entirely obvious which
- We pay a performance penalty when we have to do the type checking using
Is there a better way?
Solution 2: Using pure Object Oriented Programming (OOP)
# Code omitted from base example to focus on the change class Suv < Vehicle def markup_rate 0.4 end end
Here we override the
markup_rate method in the child class of the
The second solution is by far better as Ruby is very much an Object Oriented language. A few points why:
- Simply looking at the
Suv’s class implementation and realizing that it has a different
- The general
Vehiclehas not changed, so that class is not touched. This is a big win, as the change should be only applied in the context it is needed.
When to use is_a?
The only time I see this necessary is when our conditions based on classes that we do not own.
By that I mean
Proc or some class belong to a library we are using.