I Use Inheritance And I’m Not Entirely Ashamed Of It. Should I Be?

Some time ago I saw a video of Dave Thomas at the Scotland On Rails 2009 event. In this video he says something along the line of

“Inheritance is the work of the devil. You should not be using it, 99% of the time.”- Dave Thomas

At the time my primary ruby work was albacore – and I think I did a pretty good job of following Dave’s statement in that framework. But now I’m doing full time rails work and I’m having a hard time with this. Here’s why…

My current rails project is Rails 3 with MongoDB and Mongoid as the document mapper. This combination makes it stupid-simple to use inheritance and not feel any pain.

 

Some Context

The project is based around assessments, with questions and answers, and reporting on those answers. The system has to support multiple assessments, each with it’s own specific questions and question types. Some questions are simple drop lists, some are combinations of drop list and text boxes, and some are highly complex layouts with multiple fields and varying options based on how you answer.

To build such complex assessments on top of a common infrastructure, we set up the system with a base “Question” model. This model contains the core information that is needed to know what part of the assessment the question belongs in, what the question name and description is, and how to validate and score the answers that are supplied to the question. When we need an actual question for an assessment, though, we do not use the Question model directly. Rather, we use a specialized question type that inherits from the Question model.

For example, we have a model for the questions that are a simple list box for selection: “Selection < Question”. This model provides the additional fields that are required for the question to have the necessary functionality including an options attribute to provide the list of available options. To build an assessment, we create a Selection question and provide the list of options, then save that model to MongoDB.

The Question model looks something like this:

The Selection model looks something like this:

 

Why Inheritance?

Here’s the key to this and why inheritance is so easy with MongoDB / Mongoid: we never have to configure anything for the inheritance to work. Mongoid does it all for us.

To save a Selection question to the database, and to deserialize that question back into a Selection model, we have to do nothing more than call .save and .find, or any other data access method that Mongoid provides to the base Question model.

The document we end up with looks like this:

It’s that simple. Mongoid knows how to save and load the correct model based on the types in the inheritance chain and the _type field that it stores for us. It’s easy and we don’t have to worry about configuration or special code to make it work.

 

Why Not Inheritance? What Should I Be Doing?

This is really what it comes down to for me… I don’t understand why we shouldn’t be using inheritance in this particular situation. Yes, I know the old platitudes about “favor composition over inheritance” and in my .NET work, I have done this for many years… and in my .NET work, composition over inheritance has made my life so much easier. Tools like NHibernate, while they do make life easier for me in .NET, are painful to use with inheritance models, in my opinion. But in this system, with no pain being felt at the moment, I have to question this…

I want to know why Dave Thomas says I’m doing ruby wrong by using inheritance. Can someone enlighten me? Can someone show me an example of how I should be doing this, without using inheritance? Or is my scenario the 1% of the time that Dave would say is ok?


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in AntiPatterns, Principles and Patterns, Ruby. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Bob M.

    This is going to sound abrasive, but who gives a F*** what Dave Thomas or anyone else stating absolutes says. You are clearly using a well thought out approach which is working for you. Inheritance is still a cornerstone of OOP, despite what the cool kids say. Using and abusing are two completely different things, so trust your instinct on this one. If it’s causing you pain, challenge it. Otherwise, keep going with what you have.

  • David

    By the way – “This Gist” code snippets don’t come through Google Reader…

  • Travis

    +1 to what Bob M. said.

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    Bob & Travis – :) yeah, I’m tending to agree with that in this scenario. I’m really hoping someone can say otherwise and show why / how, though.

    David – d’oh! I wasn’t aware of that… I’ll see if I can find a better way to post code.

  • Steve

    Rails uses Inheritance all the time, just look at ActiveRecord. Your code looks fine there, if it works, it works. :)

  • Michael Foltz

    I’m no expert on the topic – although I’ve been curious about what “composition over inheritence” (C/I) is all about as well. After reading your post, I poked around to find useful examples of C/I

    Here are two posts that I found useful:

    http://www.thedotnetfrog.com/2008/05/30/choosing-composition-over-inheritance-yet-another-example/

    http://www.ronaldwidha.net/2009/03/22/a-good-example-of-favouring-composition-over-inheritance/

    Seems to me that the usefulness of C/I has more to do with behaviors than data. For instance, with your “question” scenario, if you have to implement different validation techniques for different classes – and especially if some classes share one implementation while others use another, then it’ll be easier in the long run to implement the validation logic outside of the classes and have them injected where needed (or for that matter to use a visitor pattern – which seems to be pretty damn’d similar to the idea of composition).

    As far as just specifying what fields are in various classes, I don’t see how you can make it any more straight forward than to use inheritance.

    I agree with Bob, Travis & David. If you can get around town on a moped, there’s no point in driving an SUV. Use the techniques that make the most sense, and ignore the code-guru dogma. If the reason for using a particular technique isn’t self-evident, then it’s probably not the right technique.

  • http://scottbellware.com Scott Bellware

    Without getting into whether it’s a defacto good or bad thing, there are still issues with this kind of inheritance that can make working code less “workable” – which is a productivity concern more than a whether-it-works concern.

    The ultimate issue is that it’s inheritance for the purpose of reuse rather than for hi-fidelity modeling of real world things.

    Inheritance is the strongest kind of coupling.

    There’s a Single Responsibility Principle concern in all coupling. When a class has another class coupled to it, it serves that class as its master. When a class has two classes coupled to it, and if those classes are concerned with dissimilar things, the coupled class serves two masters. Now it has two reasons to change. Now rest squarely in the sights of SRP problems. Those problems may never arise, but potential is now locked and loaded.

    DHH wrote an essay a few years ago about using modules rather than base classes in Rails if he could do it all over again, so we can’t look at ActiveRecord as a justification. That “justification” has already been debunked. In the end, factoring ActiveRecord::Base into modules didn’t happen in Rails3 because even though the problem potential was “locked and loaded” it wasn’t really causing the kind of harm that justified the level of work needed for that change. Sometimes, coloring outside of the lines is a choice we make. Making the choice unconsciously is the ultimate problem.

    You could have implemented the reuse in your model with modules, and in the end, it would have been a “truer” implementation, since the purpose of modules is more closely aligned with reusing something rather than extending identity to something. You might have chosen inheritance (sub classes) rather than composition (modules) because of class-oriented programming bias, who knows.

    Regardless of what answer you find, it’s the right question to ask of this design, and finding previously-unknown biases is like being released from shackles that you might not have known you were in. You can still choose to wear the shackles if you like the aesthetic, but you risk being tossed in jail by a cop who mistakes you for an escaped convict. Unforeseen design changes happen. Clean and proper use of patterns can help keep us free of unforeseen cost of change regardless of our aesthetic preferences.

  • Hank Fay

    I think one difference worth noting is between statically-typed languages and dynamic languages. Statically-typed system inheritance is problematic in terms of maintainability: what kind of object am I working with? In dynamic systems, inheritance opens up focused reuse without incurring costs.

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    hank – i don’t really don’t agree with that. the cost of inheritance is still there in dynamic languages, and i started seeing this today. i ended up in a situation where my one of my inherited classes needed specialization so i had to inerhit from that subclass, which created grandchildren of the original class and sprawled the inheritance tree far beyond what i had ever hoped for. it’s getting rather ugly, rather quickly.

    scott – that’s a lot for me to consider, and exactly the kind of insight i was looking for. looking back at the original decision on this inheritance chain, it’s rather obvious that we chose it because we didn’t know what else to do. it was easy to fall back into this because we had both done it in C# for many years. the dynamic nature of ruby combined with mongoid made it even easier, too.

    i’m seeing a lot of concerns bleeding out in very strange places in my code right now. i’m not sure if the inheritance is a cause or a symptom, or both at this point. i need to re-evaluate a lot of this code and throw together a few prototypes to see what it will take to move away from inheritance.

    more importantly, i think, i need to re-evaluate some of my abstractions and find out why so many of the concerns that my classes have feel like they’re being tossed in as after-thoughts instead of modeled as real parts of the system. perhaps the inheritance is making it easy for me to make bad decisions on this front. it certain feels that way.

  • http://twitter.com/jakescott Jake Scott
  • http://johnteague.lostechies.com John Teague

    I don’t think there’s anything wrong with inheritance when used appropriately. If you’re inheritance structure is simple with only a few well contained branches then it’s probably fine. But as you mentioned in you’re reply, if you get a lot of special cases, then the abstraction might be wrong. At that point, strategy pattern, or in your case modules, sound appropriate.

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    i did a few experiments with my code and it looks like i’m stuck with inheritance, because of Mongoid. i was not able to get it working from a module, unfortunately. so i’m either stuck with inheritance or i have to duplicate the required fields in all of the objects that would use a module… and that’s worse than inheritance, i think.

    we’ve also had some serious monkey wrenches thrown our way since i wrote this, and it looks like this code will have to sit for a while. this is probably a good thing, though. when we do come back to it, i’ll have a better idea of how painful and/or poor the structure is – i’ll have forgotten what i was thinking by then, and having to relearn it will expose a lot of problems i’m sure. i just hope i can find good solutions when i finally do see the problems.

  • StarTrekRedneck

    That was an interesting transition. In the original post, it felt like Mongoid was sort of praised for it’s power and convenience. But in the last comment, Mongoid is criticized: “I’m stuck…because of Mongoid.” I don’t know if that’s exactly what you mean or think, but I’ve made transitions like that in my career, where one solution seemed the pinnacle of elegance and value, and later turned out to be inflexible and a liability. I guess we gotta keep learnin’. Education ain’t cheap, but the alternative is far more expensive.

  • Charles Gardner

    Woot Woot! Agreed!

  • Charles Gardner

    How are you templating with the Inheritance?