Update: The eBook is released! - Latest Update: 19/01/2023
This article will explain design patterns, which we use daily, with smaller (over)simplified illustrations. Also a big shout out to Mahdiye Ijavi who made a big chunk of the design. The original version was not that pleasing 😉.
This blog post will get updated with new patterns as soon as I did them 😉 so stay tuned for more content!
eBook
Direct download link: Patterns oversimplified.pdf
Behavioral
The famous mediator pattern. Imagine you are in a restaurant and order food. You don't go directly to the kitchen and neither does the kitchen directly serve the food to you. All interaction is done via the waiter aka the mediator. You don't have to know the kitchen and the kitchen doesn't have to know you. Both parties are completely decoupled.
Here is the second pattern I want to show you the strategy pattern. For example, you want to go to the airport. There are multiple ways aka strategies you can follow. You can either take a taxi or bus or you can take a bicycle. You will decide that at the moment depending on your strategy (cost, traffic, ...).
The cool thing is that you can extend behavior via composition rather than inheritance. So we can see that it goes well with the SOLID principles.
Normally we use the pattern if we have multiple algorithms, which differ in detail but serve the general overall goal. Another viable example would be sorting a List. You can use bubble sort or quick sort, or or or. The goal is the same: Sorting the list, but it differs in how we do it.
Creational
With the singleton pattern we restrict access to instantiate an object by a single instance. Singleton's are preferred over global variables but they can quickly get out of control. A special form is the "lazy singleton" where the creation of the object is deferred until the first client uses it. Singletons also violate the single-responsibility principle, because they are responsible for enforcing their uniqueness, alongside their normal functionality. With modern frameworks like ASP.NET Core you can use the power of dependency injection to mark instances as singleton, which is oftentimes the preferred way.
Structural
A proxy acts on behalf of the object it wraps like a delegate. The core goal is to add additional functionality. Entity Framework for example uses the proxy pattern to lazy load properties from the database. When you call the getter of an object, you actually call the getter of the proxy, which then goes to the database and fetches said property. Proxies can also be used to control who has access to the underlying object.
The adatper pattern helps us to communicate between seemingly incompatible objects. Every mapper you wrote in your code is an implementation of that pattern.