I recently finished reading the popular Design Patterns book. As I was reading, I noticed that many of the examples used in the book were a bit hard to relate to. For several patterns, they used the example of building a WSISYG editor, something with which I don’t have much experience. Many of the examples in the “Known Uses” section of each chapter were also a little outdated. As a way to help me understand these patterns in detail, I wanted to create my own examples, modeled after real-world situations. This post will go through a few patterns and will attempt to find similarities between abstract object-oriented design patterns and more tangible real-life examples.
Creational patterns deal with how objects are created. Controlling how objects are built can have a big impact on improving the design of a system. It can allow a system to be independent of how its objects are created.
One such creational pattern is the Builder pattern. This pattern separates HOW an object is constructed from its actual representation. This will allow us to use the same general construction process to create different representations.
One real-world example of this pattern occurs in the car manufacturing industry. When building a certain car according to specification, the manufacturer chooses several options about that car. These could include the color, the engine, and any additional features of the car. Here is the class diagram for this example:
In this example, the client (the manufacturer) interacts directly with the builder to manufacture cars. The advantage of using this pattern is that we can construct cars with different characteristics using the same builder, and by specifying different parameters to the builder. We have very little coupling between the client using our builder and the product being built.
This category of patterns are concerned with how classes and objects are built and composed as part of a larger system. Several of these patterns in this category also help coordinate independently developed classes into a single structure.
One such structural pattern is the Facade pattern. Facade provides a single unified interface that encompasses several interfaces in a subsystem. It acts as a funnel to expose a single interface to many clients, and hide the actual subsystem that is responsible for doing the work requested. Here is what I mean:
From the above diagram, we can see that the Facade coordinates several subsystems behind the scenes, but gives the appearance of a single, simple interface to clients. One example of this Amazon’s 1-click ordering system. When ordering items, a customer is presented with a simple interface to purchase an item, but there are several complex processes running to enable this item to be purchased.
A Customer ordering an item for this system is spared the details about what goes on “under the hood”. The Facade takes care of coordinating the actions of the different processors in the subsystem. Another advantage of this pattern is that if a certain piece of the subsystem were to change, the Customer making the order would be unaffected and unaware.
This group of patterns involves the interaction between objects. These patterns aim to improve the distribution of responsibility between objects and increase the overall flexibility of the system.
The Strategy pattern was, to me, one of the most intuitive patterns in the book. Strategy is used encapsulate an algorithm as an object to simplify interchangeability. The client can choose the appropriate Strategy at run-time based on factors that are not available at compile-time. In addition, there is the additional benefit that the client is shielded from any complex logic, which can be outsourced to the Strategy class.
One delicious example of this pattern is with eating Oreos. As everyone knows, there are a myriad of ways to consume this Great American Cookie. If we consider the process of eating this cookie as an algorithm, we can model the Strategy pattern pretty easily:
Here we have three possible strategies for eating Oreos: dipping it in milk, eating a whole cookie at once and (barbarically) split it apart into two pieces and lick the cream off. Our client is a hungry child who will be making a run-[ing around the house]time decision as to how to eat the cookie. Rather than being burdened by the complexity of figuring out how to eat an Oreo, our hungry child can simply offload that knowledge to Strategy classes that encapsulate the Oreo-eating algorithm.