railway

Inheritance, Pseudo Classes and Mixins Gone Awray

Somebody asked me a few days ago to give them an opinion on how they approached implementing a little library dealing with SVG. While it seemed mostly okay, something struck me: Although there were multiple types of points/shapes, functionality was mostly shared using modules. That by itself isn’t bad – it’s only bad if inheritance would be the better, more natural choice.

Rubyists Love Modules

We Rubyists love modules (mixins) because they allow us to share code between non-related types. In contrast to other programming languages – Java being a prime example –, Ruby modules have a major advantage: Not only do they let us share an interface but they also enable us to inject actual implementation. This comes at a cost: Because sharing implementation is so remarkably easy using modules, at times, we tend to overlook when we should actually favor inheritance.

Mixins Gone Awry

Take a look at this (fake) example, based on the SVG stuff mentioned above:

module Drawable
  def draw
    # ... whatever ...
  end
end

class Triangle
  include Drawable
end

class Rectangle
  include Drawable
end

class Square
  include Drawable
end

One of the things to remember about object-oriented programming is that we’re trying to depict reality by using objects. While using mixins certainly is possible, we do have actual real-life inheritance here: Triangles, rectangles and squares are all shapes; and squares are, of course, special rectangles.

If there is a relationship in reality, then we shouldn’t destroy that relationship in OOP.

Detecting Real Inheritance

The ultimate helper to find out whether or not one should use inheritance is quite simple: You should use inheritance for is a / is an relationships. “Rectangle is a shape?” => Yep. “Square is a rectangle?” => Yep.

Unfortunately, we can’t really count on Ruby’s own is_a? method because it also takes into account modules:

square = Square.new
square.is_a?(Rectangle) # => true
square.is_a?(Drawable)  # => true (and it really *is* true)

Whenever this is not the case, you’re essentially doing fake inheritance. Some prime examples can be found in Rails: Inheriting from ActiveRecord::Base is wrong – at least in the object-oriented sense.

class Person < ActiveRecord::Base
end

clemens = Person.new
clemens.is_a?(Person)             # => true
clemens.is_a?(ActiveRecord::Base) # => true (but only in Ruby/Rails, not in reality!)

In reality, a person just is not an ActiveRecord::Base (what is that anyway?).

Other object mappers – DataMapper, MongoMapper, CouchPotato etc. – are doing it right:

class Person
  include CouchPotato::Persistence
end

While certain persons certainly are couch potatoes in its metaphorical sense, they aren’t for real (again: What the hell is a CouchPotato::Persistence?) – so there should not be any inheritance.

Pseudo Classes

Case and point: ActiveRecord::Bases don’t exist in reality – I’ve never seen one walking down the street. So ActiveRecord::Base, in my opinion, is a pseudo class.

If something doesn’t exist in reality, there shouldn’t be a class for it.

On the other hand, I’ve certainly seen persons walking about and I’ve drawn a couple of squares and triangles in my time. These most certainly are real classes.

Using mixins is just fine: Certain objects share behavior – like being persistable. However, you should name them accordingly. I like Java’s often-seen approach here of suffixing its interface with able: Runnable, Serializable etc. It can be found in the Rails world, too: Devise uses it extensively. So in my opinion, the better name for the above would be CouchPotato::Persistable.

Summary

When building a class hierarchy, try to think about reality: Are both classes real (non-pseudo) classes and is there a real, actual relationship between the classes in question? If so, use inheritance. If not, use mixins (and name them properly to reflect that they provide certain functionality). It’s as simple as that.