“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.
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:
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?