Saturday, December 26, 2009

Design Review and Delegation Vs. Inheritance

This week I want to discuss another bullet from my code review checklist

Consider delegation over inheritance.  I have had some pretty heated debates on this topic in the past and I will even take it a step further and say the rule should state that we should favor delegation over inheritance.  Let’s consider the following classic (and intuitive) person/student/teacher example.  A class diagram might look something like this

image

Person is our base class; Teacher and student derive from person.  We can say that a student Is A person and that a Teacher Is A person.

The up-side of this design is it is simple, intuitive, and it will perform well.  The down side of this approach is that there is a tight coupling between the derived classes and the base class, we are breaking encapsulation by making the functionality of the derived class dependent on the functionality in the base class, and we may even break encapsulation on our Person class by using protected or public access modifiers where we normally wouldn’t do so. 

The problem with breaking encapsulation is that making a change to one class (our base class) can cause unintended changes in other consuming classes (our derived classes).  I can’t begin to tell you how many times I have seen a seemingly small change to a base class break huge pieces of an application.

Now let’s consider using delegation rather than inheritance.  We will change our class diagram to look like the following:

image

We can say a teacher Has A person and similarly student Has A person.

The down-side of this design is that we have to write more code to create and manage the person class and thus the code will not perform as well (though in most cases I suspect the performance hit is negligible).  We also cannot use polymorphism - we cannot treat a student as a person and we cannot treat a teacher as a person.  The up-side of this design is that we can decrease coupling by defining a person interface and using the interface as the return type for our Person property rather than the concrete Person class, and our design is more robust.  When we use delegation we can have a single instance of a person act as both a student and a teacher.  Try that in C# using the inheritance design!

 

6 comments:

  1. Hi Tim!

    One of your arguments against inheritance is that a change to the base class can break the application. How does that differ from a change to the person class using delegation?

    Jim Brandley

    ReplyDelete
  2. Hi Jim,

    I do not really have an eye opening ah ha answer to your question. Lets consider 2 design patterns, the template method (inheritance) and the strategy (delegation) pattern. The intent of both patterns is to separate the detailed implementation of a generic algorithm.

    The Template method pattern uses a base class to define an algorithm and the derived classes implement any specific details. If we change the base clase and inadvertently introduce a bug, all of the derived classes will be broken. Determining if the bug is in the base class or derived class is not trivial. To fix the broken classes, we must recompile the derived classes.

    The Strategy pattern uses the dependency inversion principle to abstract the algorithm. We basically inject an algorithm into a consumer - the consumer delegates the work of executing the algorithm to the injected object. Suppose we introduce a bug to to the injected class. All classes using this class will not behave properly when they use the injected class, however they are not "broken" in the sense that they don't need to be recompiled to fix the problem.

    This is a soft example for a soft rule. I grew up in the classic VB world where implementation inheritance did not exist. When I moved to .Net I think I made the mistake of over using this shiny new inheritance toy I had in my toolbox. Inheritance is very powerful and certainly has it's place. With that power comes some danger in that small changes to base classes can have a far reaching impact on our code base. Delegation is a bit less powerful but the trade off is that our objects are a less tightly coupled and therefore a little easier to maintain.

    Thanks for the comment!

    Tim

    ReplyDelete
  3. A nice marriage MIGHT be to use delegation but also introduce a "PersonInterface" that all three classes implement.

    The user classes simply call the person to have it do the gets/sets.

    ReplyDelete
  4. I'm sorry Tim but I just have to disagree with you here.

    I would hail from the camp that says that yes, you have to be very careful when defining base classes. And I would hardly ever use the keyword public.

    It doesn't make sense to me to throw out polymorphism in favor of a perceived advantage in multiple characteristics.

    So what's my solution? How could we be more elegant?

    My solution would be, if you're indeed writing in C# and you want a person to be both a student and a teacher, to instead write a new "GradStudentTA" class in Eiffel.NET or equivalent and use multiple inheritance to bring in the functionality of both the student and teacher classes. In .NET, you can then access your classes from the language of your choice. If you had a naming conflict on a member, remember that you can rename them as you inherit in languages such as Eiffel.


    Sincerely,

    Mike Sahyun
    Minneapolis, MN

    ReplyDelete
  5. Hi Mike, thanks for taking the time to comment and offer up an alternative. There is certainly room to disagree on this one. Since I have never worked in the Eiffel Development Framework I'll let your alternate suggestion speak for itself.

    ReplyDelete
  6. I have personally watched people using inheritance over delegation break an enterprise level application. I was researching it to make sure I have some good buzzwords when I defend myself in my opinion of favoring delegation, and I found this page. Its been a few years since you posted this, but I just thought I'd tell you, you are so right. =)

    ReplyDelete