Some people adhere strongly to design patterns. They have read the “Gang of Four” book and are now convinced that there should be a design pattern used in almost every piece of code. Others have never heard of that book and are unknowingly writing code that almost resembles a design pattern but does not clearly communicate which pattern is used or has some other flaws. I will try to describe a pragmatic approach somewhere in the middle between those extremes and why I think it is best.
To get possible misunderstandings right out of the way: I don’t think the GoF book is bad, not by far. It is a standard lecture for object-oriented programming and has been so for 20 years now. However, since it is that old, it does not take into account more recent discussions. So I suggest you read it, but not everything in the book is cast in stone.
You should also consider reading “Head First Design Patterns” which will give you good information about the commonly used patterns as well and does not read as dry as the GoF book. Whichever book you choose, make sure you know the commonly used design patterns and where they are applicable.
When to Use Design Patterns
Having read the book and knowing about design patterns does not mean one should try to find the design pattern in every piece of code written. The patterns the book mentions have been found to often occur in object-oriented design, which means they are broadly applicable to a range of design problems. But that does not mean that list is complete. There are other constellations of classes that occur relatively often, while not as often as the patterns that made it into the book, and some applications need a class design that is more or less unique.
Design patterns are tools, the commonly known design pattern books are general purpose toolboxes. They can be applied in a wide range of problems, but they don’t solve every possible problem in the best possible way, and some problems they cannot solve at all.
That does not mean you should always roll your own class design. When you have to design a solution for a given problem, at first determine if a known design pattern could be a good fit, and only fall back to a custom design if there is none.
Prefer using known design patterns, but don’t be dogmatic about applying them everywhere.
When Not to Use Design Patterns
As always, simplicity is key. If a simple implementation with one or two functions or classes suits your needs, there is no reason to write a full-grown pattern implementation that fits one of the patterns in the books.
As written above, the original GoF book is over 20 years old. Nowadays, the use of at least one of the patterns is controversial. The most controversial pattern is the Singleton pattern. It is often viewed as an “anti-pattern” because it can complicate or even prevent testability and modularity of the software. I have the Singleton pattern on my list of possible blog post topics, so I probably will write about it in more detail sometime in the future.
When considering to use one of the patterns described in the GoF book, at least have a quick web search on it to get an idea if it is considered good or bad style to use it. When working in a team it should be part of the style guidelines if the use of certain patterns is acceptable or not.
How to Use Design Patterns
Equally to when to use design patterns, some people get very restrictive about how to implement them. As the term suggests, patterns are not dogmatic rules. There can be different ways to implement a design pattern, and the implementations shown in the books are only examples. In fact, for many patterns, the GoF book mentions different implementation variants.
On the other hand, the books do mention certain rationale for certain implementation details. Don’t easily disregard those rationales, but feel free to consider variations that don’t violate them.
Patterns are guidelines, not strict rules. There is no single right way to use them.
When implementing a known design pattern, make sure to name the corresponding classes and functions accordingly. Also, make sure to have a representative for all major components of the pattern. That way everybody who reads the class names can immediately recognize which part they play in the overall design. After all, we write human readable code mostly for the maintainers, not for the compiler.
Patterns are most useful if they simplify the way you and the maintainers of your code think about the class design. Use them to structure your thoughts and to communicate your intentions, not to complicate your implementation.