Why design patterns suck

A rant on common misuses of coding design patterns

, 21.08.2017
Never underestimate the added complexity of a coding pattern compared to it's benefit. Always check if it's worth it or not. There are no extra points for a high number of patterns used.

Yes, the title might be a bit provocative, but it grabbed your attention, didn't it? And at least in my opinion, it is very much true these days. Many developers, especially those fresh from universities tend to add coding patterns as if there was a competition for it. And this is bad for your code. Patterns have a high cost. They add complexity to your project that can actually make your code harder to maintain and evolve. Exactly the opposite of what a pattern is supposed to do.

Let me give you a short example.

deep within our project, we have some logic to calculate a special number. The code for it could look like this (Don't focus on syntax, it's not real code. But the logic should be clear enough):

class NumberThing {
    public function crunch($number) {
        return $number * PI + 2; // do something with pi constant
    }
}

$thing = new NumberThing();
print $thing->crunch(123);

A very short class with not much complexity. Now a common scenario is, that at one point a developer criticizes the lack of flexibility and the hard coded logic and starts refactoring and introducing patterns. In this case, dependency injection and a hint of strategy pattern and factory pattern leads to this new version:

class NumberThing {
    protected $cruncher;
    public function setCruncher( ICruncher $cruncher ) {
        $this->cruncher = $cruncher;
    }
    public function crunch($number) {
        return $this->cruncher($number);
    }
}

class PiCruncher implements ICruncher {
    public function crunch($number) {
        return $number * PI + 2; // do something with pi constant
    }
}

class NumberThingFactory {
    public static function create() {
        $thing = new NumberThing();
        $cruncher = new PiCruncher();
        $thing->setCruncher($cruncher);
        return $thing;
    }
}

$thing = NumberThingFactory->create()
print $thing->crunch(123);

You could bloat the code even more by abstracting the factory and creating more interfaces, but you get my point.

Yes, at first glance, the new code looks much more structured and flexible. But what did we really accomplish? We added 20 new lines of code to make 1 line of code changeable in the future. And even if we need to change it, we need to create a new cruncher class and change or create a factory for it. All to avoid simply changing the formula in our initial class. The application now has become a bit slower, documentation and guide lines for changes in the cruncher are more complex and if we get a new developer, we need to tell him how the application structure is supposed to work. Not much overhead in this sample, but we were examining very basic logic. Now imagine a bigger project with an already complex code base and possibly a ton of other patterns or compound patterns in use.

And what's even worse, the above example is not what you will find in practice. Above, patterns are added fairly clean and nearly by the book. Now look at this much more common version:

class NumberThing {
    protected $picruncher;
    public function setCruncher( ICruncher $cruncher ) {
        $this->picruncher = $cruncher;
    }
    public function crunch($number) {
        return $this->cruncher($number); // do something with pi constant
    }
}

class PiCruncher implements ICruncher {
    protected static function $pi;
    public function __construct($pi) {
        $this->pi = $pi;
    }
    public function crunch($number) {
        return $number * $this->pi + 2;
    }
}

class NumberThingFactory {
    public static function createWithPi() {
        $thing = new NumberThing();
        $cruncher = new PiCruncher(3.1415);
        $thing->setCruncher($cruncher);
        return $thing;
    }
}

$thing = NumberThingFactory->create()
print $thing->crunch(123);

Now we have an abstraction with it's associated cost in complexity and don't even get the benefits of flexibility and decoupling.

The NumberCruncher named it's variable "picruncher", which basically forces it to using the PiCruncher, making the decoupling ineffective. Yes, it is still using the interfaces and you could still use another cruncher class, but any developer having to find out that the variables do no longer contain what their names imply will most likely have a chat with you after work, in a dark alley.

Second, to make our PiCruncher even more flexible, someone decided to make the pi constant changeable. Besides being pointless, we have added a fixed coupling to our factory that now needs to call a constructor that is no longer generic. It also has to contain essential information for our logic, which absolutely does not belong there.

And my point is...

A coding pattern always has a certain benefit and a cost associated with it. Always analyze this cost very closely and only after checking all of it's impacts on your project, decide if it's worth it or not.

Yes, patterns are good, but you need to evaluate them. Especially when the chances of using them wrong are so high.

Something to read

Head First Design Patterns: A Brain-Friendly Guide
A very hands on approach to the most important patterns. Easy to read and without unnecessary elitism.

Something rather not to read

Design Patterns: Elements of Reusable Object-Oriented Software
For many developers the holy grail for coding. In my opinion a book that may surely be the definitive reference on patterns, but without any guides on how and why to use it possibly very dangerous to your code.

Tobias Bulla

Lead Technical Development

Das ist aber kein strukturierter Content