Design Patterns: Easy to Write, Hard to Recognize
Have you made this classic programming mistake?
You’re 2,000 lines into developing this awesome framework that is going to make your team’s life so much easier when you realize that you’ve just done it:
You just reinvented the wheel.
This happens because you are a programmer, and therefore you are lazy. You love to write code, but you hate reading it. Reading code is for computers, you tell yourself, and you are, after all, made of carbon not silicon, so there. Plus, you write Perl, so it’s basically impossible to read anyway. But back to design patterns…
Design patterns are great because they provide a common vocabulary that helps developers communicate complicated ideas with a few short words like “Observer” or “Singleton”. If you’ve ever read a book on design patterns, I bet the first thing you did afterwards was implement one in your current project. That’s what I did. If you’re thick like me, which you obviously are, because you’re reading this drivel (be honest with yourself), then you probably missed the main point of design patterns. The main point of design patterns isn’t to give you something to code up. They exist to help you recognize patterns in code that already exists so you can reuse it in your project. Sure, you’ll implement one once in a while, but you really ought to be using them more than you’re writing them.
It’s story time
I recently spent some time with a friend reviewing his software project. It was a Qt/C++ user interface project in its infancy. He’s a super smart guy, and a good programmer. He knows Qt pretty well, and he knows that one of the things that makes it great are its signals and slots. Signals and slots are awesome. They make it so you can easily interact with the Qt library with very little work. They tell you when a user clicked on a button or changed some text in a text box, and things like that. In a nutshell, they let your code “observe” the state of things. Back to my friend and his model/view. For my friend’s model, he implemented the observer design pattern. This required each of his classes to implement an Observer or an Observable interface. He used C++ vectors to store pointers to each Observer so he could notify them of when things changed, and wrote all the code to add and remove new Observers on each Observable object. He was careful not to leak memory, or hang on to deleted Observer instances in each Observable. Great, right?
Head scratching time
Yes, my friend reinvented the wheel. Qt already provides the observer design pattern. Qt calls them signals and slots. Yes, you can define your own signals and slots, and Qt manages all the “observer” stuff for you, and I guarantee Qt does it better than my friend’s code (less memory, faster, etc etc). So now my friend has two observer design pattern implementations in his code base, one from Qt and one he coded up himself. Which one do you think has fewer bugs? The one that’s been around for 10 years and used by thousands of people, or the one he hacked together in a few days at work? Oh, and by the way, Qt’s signal/slot system handles crossing thread boundaries too. I’m telling you, signals and slots are awesome.
Why does this happen?
The answer is chilling: Those who fail to read the documentation are doomed to reimplement it (apologies to Mr. Churchill). The next time you are reading an API documentation, try plucking out the design patterns, and boiling the library down to its essence. What does this library really resemble, at its heart? How could I describe this feature in 3 words or less? You may be surprised by what you see that has been there all along.