Misconceptions about code reuse
Design

Misconceptions about code reuse

We all have been taught that reusable code is good. We all know why we should do it but there are some misconceptions about how to do it.

There are two common misconceptions about reusable code:

  • Using inheritance in order to achieve code reuse.
  • Creating a static utility or helper class in order to reuse methods.

Mostly these misconceptions are the result of procedural thinking. People usually understand how certain object-oriented principles work. However, it is seldom taught what is the purpose of such principles.

Inheritance is not the way to achieve code reuse

While writing code you might find potential methods to extract for code reuse. You might decide to pull the method up in the class hierarchy in order to make it available in all derived classes.

The other case is that you identify shared code between two or more classes and decide to create a super class in order to reuse code between the classes.

There are some problems with using inheritance for code reuse:

  • The interface of the classes become easily bloated with unneeded methods.
  • The methods can only be reused inside the class hierarchy.
  • Methods are not easily extendable and added genericity increases complexity.
  • Inheritance reduces options on sub classing as you cannot sub class something else.
  • Inheritance ties you to the implementation of the super class.

A better way to achieve code reuse is to favor composition over inheritance. This means that instead of just extracting methods you should be identifying new classes. Composition keeps your options open, you can reuse different implementations and you can change your mind easily.

What inheritance is really meant for

Inheritance is used to categorize things and allow us to use polymorphism. You can build hierarchies of concepts at different levels of abstraction. Polymorphism allows same piece of code to manage all objects in a category independent of their implementation.

The main reason to use inheritance is software design and not code reuse. Derived classes can incidentally reuse code from the super class but it is not a driving force to use inheritance.

Utility classes are procedural programming

Utility classes are the result of trying to follow the don’t repeat yourself practice. Utility classes are an indicator of laziness and lack of domain knowledge because it is easier to put common code into utility classes rather than trying to identify a proper place for it.

Writing utility classes is better than copy pasting code but you can do better. Let’s see what is wrong with static utility classes:

  • Utility classes are not object-oriented but procedural which violates encapsulation.
  • Dumping methods that have no better place into utility classes mixes responsibilities breaking the single responsibility principle.
  • You cannot extend static methods or implement interfaces.
  • Static methods can make testing hard or using test doubles very complicated.

Instead of just dumping code as static methods in some placeholder class you should identify responsibilities. Create proper classes out of these responsibilities and call the classes by that name.

This not only puts behavior into a single place but also makes it easier to understand what the class does. It will probably lead into several smaller classes having well defined responsibilities instead of one bloated utility class.

For example, MessageParser and MessageHandler spell out clearly what they are supposed to do while MessageUtil does not.

Utility classes often also exist for common reasons. Chances are that you are not the only one trying to write them. It is very likely that you can use some open source libraries instead of reinventing the wheel.

8 thoughts on “Misconceptions about code reuse

  1. Tei

    OOP is a programming style, not a religion. But if it where, the only real sin would be to do something without understanding why, and doing it without getting profit from it. If you are lazy, and you something lazy, and that way save yourself some work, I applaud you. But if you are lazy, and your lazyness result in more extra work, I booo on you. So being lazy is not a sin, working more than necessary is the sin.

    • Thank you for your comment. Everything depends on context of course. While my article might be a bit provocative (someone might call it even a bit black and white) I still believe these are the two most misused cases. I’m not trying to convince anyone to always do it this way. The key factor is to think why are you doing something the way you are.

  2. Severin Pappadeux

    procedural which violates encapsulation.

    Encapsulation of exactly WHAT entity is violated? From your PoV, all functional style of development is violation of some kind of encapsulation, n’est-ce pas?

    • Thanks for the comment. I’m just being ignorant. Let’s just say a lot of procedures pull out data of an object to do something while it would better fit in the object itself. Maybe it’s more like a violation of the Tell, Don’t Ask principle rather than encapsulation per se.

      • It is a bit more subtle. Read upon Effective C++ Item 23. It gives a good explanation of why free functions in fact are better for encapsulation. But that is easily a black and white statement so let me elaborate. I think were we agree is that if free functions were free to fiddle with the internals of a class that would be really bad for encapsulation.

        Rather classes should define a minimum set of methods which allows the class to be used correctly and invariants to be maintained. E.g. a stack should expose push and pop but not the fact that it is implemented as say a linked list or array.

        The point of free functions is that since they are not allowed to modify the internals of a class if you made sure you date is private, they can be reasoned about very easily. If you got a class with 110 methods, vs a class with 10 methods and 100 free functions, which one would be easiest to modify? With the first case, in theory any of the 110 methods could fiddle with the internals of the class. In reality we have the same as a program with global data and 110 free functions.

        In the latter case however we know that we only need to understand 10 functions to modify the class. As long as those 10 functions expose the same functionality we know that those 100 free functions will function exactly as they have before.

        Of course what makes sense varies with language. This is good advice in C++, but in e.g. Swift it is less of a problem. You methods to classes in separate files and since swift access modifiers are file and module based and not class based, you know that methods added as extensions in separate files can not possibly be fiddling with the private parts of the class they extend.

        • Yeah, I agree. It depends on the language used. Maybe my post was a bit too general as such. I should have made some distinction between different programming paradigms and languages. Thank you for the well thought out reply!

  3. Hey Arho,

    I thought this was well said and well written. It’s something I have also been thinking about lately and – as a result – I tend to avoid inheritance where I can (especially in CoffeeScript, where it seems to be a huge hammer and everything looks like a nail).

    It’s always tricky to come up with examples for this kind of stuff though, right?

    Jaco

    • Thank you Jaco. You are right, coming up with examples can be difficult. This post would have very much benefited from some proper code excerpts that would have cleared up some points that can otherwise remain a bit vague or misleading.

Leave a Reply