This post originated from an RSS feed registered with Ruby Buzz
by Eric Hodel.
Original Post: Subclassing vs include
Feed Title: Segment7
Feed URL: http://blog.segment7.net/articles.rss
Feed Description: Posts about and around Ruby, MetaRuby, ruby2c, ZenTest and work at The Robot Co-op.
[W]hy make the cached model a class to extend instead of a module? Whether or not a model is cached should be an implementation detail, and shouldn’t define the hierarchy for a class. I know I would rather not use the power of (single!) inheritance just to cache something, when a mix-in should be plenty powerful to do it.
The short answer is:
Using a class the correct way to overlay features on top of another class.
Here’s my long answer:
When you use a class you get super, and super is a beautiful thing. It automatically walks your class’ ancestors and calls the right method in the right order.
A module doesn’t have this property so you can’t use it to overlay features on top of a class. The class’ implementation will always be called before the module’s implementation.
Now you’re going to say something about using alias to shuffle methods around. You could do that, but you’ll have to do this for each method you want to overlay (five in CachedModel) which involves lots of extra typing “do_the_thingy_without_the_stuff” that you could have had for free (and more-prettily) with “super”.
You also get another problem that may cause subtle bugs. When you use alias to overlay features the order of execution is dependent upon the order the files are required in! Intentionally writing code where the behavior may change file load order gives me the heebie-jeebies.
Having to do all that work to get the benefits of a subclass tells me that a module isn’t powerful enough to do what a subclass can, so a module isn’t the right way to add a cache.
I avoid* using modules to overlay features of a class, but do use them to add orthogonal or complementary features. Typically when I write a module it ends up being used like Comparable, Enumerable or Singleton. When I need to do something invasive a subclass is better.
Finally, making the argument that adding caching to ActiveRecord::Base via a subclass shouldn’t define the inheritance argument is very subjective. I could justify using a subclass by saying that ActiveRecord is a data storage class, and CachedModel is just another data store. If you want caching, inherit from CachedModel. If you don’t want caching, inherit from ActiveRecord::Base.
Except void or where prohibited by law. Your milage may vary. Break glass in case of emergency.