Anti-patterns in software development

Being able to recognize patterns and anti-patterns in source code is important when your software must be understandable and maintainable. Thinking in patterns makes it easier to reason about the code at a higher abstraction level.

Read more: Anti-patterns in software development

What are design patterns?

First, let’s understand what a design pattern is. In software engineering (and also in a number of other fields), a pattern can be described as a repeatable solution direction that can be applied to a specific set of problems. The classic book Design Patterns describes that a pattern consists of four elements:

  • A pattern has a name. That makes it possible to communicate our proposed solution to others, to write it down, in short to work with it.
  • A pattern solves a problem, otherwise applying the pattern wouldn’t be useful. It is just not sensible to introduce a pattern to achieve nothing.
  • A pattern provides a solution: how is the problem solved in a way that can be broadly applied? Note that a pattern is more of a solution direction than a direct solution, as it solves a class of problems.
  • A pattern results in consequences: a pattern has a set of trade-offs. For example, many design patterns increase flexibility at the cost of increased design complexity. So this means that patterns should be applied judiciously.

Maybe this sounds all a bit abstract, so time for an example.

Singleton pattern

A well-known pattern is the Singleton. It is a very simple pattern that solves the problem of ensuring that at most one instance of an object is created system-wide. It does it like this:

public class Government {
    private static Government _instance = null;

    private Government() {
        /* The only constructor can only be called from within the Government class */
    }

    public Government getInstance() {
        if(_instance == null) {
            _instance = new Dispatcher();
        }
       return _instance;
    }

    /* remainder of the class here */
}

This construction puts the Government class in control of creating itself, making it impossible to create instances from outside the class. All access to the Government class is through the getInstance method, which ensures that at most one instance is created. This is the whole point of the Singleton pattern, it is the problem that the pattern solves. This example also shows that the pattern describes a solution that can be applied over and over again for other classes, as you can make any class a Singleton by applying this pattern.

The Singleton pattern also has a number of consequences. For example, it ensures that a single, unique instance of the class exists. Working with this instance is usually easier than exposing static members. It is also easier to use than a set of global variables, which may pollute the name space. It also facilitates lazy initialization: in this example, the instance is only created when first needed.

However, many developers object against the use of the Singleton pattern. They have a number of reasons for that:

  • Singleton makes it easier to encourage bad design: instead of passing the required object, just call getInstance from everywhere.
  • Although client code does not have to keep track of the dependency anymore, all classes that call getInstance do have a dependency on the concrete Singleton type. It is not possible to substitute it for something else, for example a mock. All these classes are tightly coupled to the Singleton class.
  • The problem that the Singleton solves is often not a real problem even though it may be perceived as such. You may think that always only ever a single instance is required, but some day the scope of the system expands and you may discover that multiple instances are needed. At that point, all classes that use the Singleton must be refactored.
  • This often becomes clear when you want to implement unit tests for a class that uses a singleton. This is difficult, since the singleton contains hidden state that is carried over between tests. Also, in case a singleton contains a system resource or something, you might not want to access it in a unit test. So you need to mock it, but you cannot, exactly because of the Singleton. Solutions exist for this, but they drag you further to a brittle design.

Therefore, many developers, including me, see the Singleton pattern as an anti-pattern. They think it is better to not use Singleton at all, since the drawbacks outweigh the advantages.

What is an anti-pattern?

Like a design pattern, an anti-pattern also is a template of a solution for a problem that can be applied to many situations. However, anti-patterns provide solutions that are regarded ineffective or result in more problems than they solve. Or they solve something that is not a real problem.

Although design patterns refer specifically to the application design, especially how classes relate to each other, anti-patterns are commonly also used in a broader sense. They may be used to indicate design flaws, but for example also indicate shortcomings at organizational or process level.

Related to anti-patterns are code smells. Sometimes the terms are used interchangeably, but a code smell is basically more of an indication that there may be a problem in the code, but this depends on the actual context. As an example, consider a very large method. This is a code smell because large methods are normally hard to maintain. But when the method just does a lot of static configuration, then this may be favored over the available alternatives. A code smell does not prove something wrong, it is more like a warning sign.

Leave a Reply

Your email address will not be published. Required fields are marked *