Mark Houston, Software Engineer II
In the previous blog post from this series, we introduced design patterns as a means for solving recurring design challenges, and we discussed the purpose and application of creational patterns. In this post, we will continue our study of design patterns by examining the role and principles of structural design patterns. We will also explore how a selection of structural design patterns can be applied to our mom-and-pop sandwich shop to represent real-world applications.
Structural Design Patterns
Structural patterns depend on entities’ associations with one another to create and extend objects based on their relationships and shared properties. With an understanding of how entities rely on one another, a developer can intentionally design solutions that take advantage of known relationships to adopt properties and logic across object definitions. Structural design patterns can come in handy when:
Multiple entities use similar or identical properties
An entity’s definition relies on the logic of another class
An object has a shared parent class with another entity or has child classes
To begin our exploration into structural patterns, we will look at real-world examples for both the adapter and composite patterns. Although we will only explore these two patterns in this reading, a variety of other structural patterns exist, including the decorator, bridge, facade, flyweight, and proxy patterns. Each of these patterns shares the common goal of simplifying the design of large objects and structures through commonalities and relationships among them and defining repeatable characteristics and extendable functionality.
The Adapter Pattern
In complex systems, it is common for situations to arise in which unrelated entities need to communicate with one another or work together. To assist with this task, the adapter pattern suggests the implementation of an entity that acts as a bridge between the two incompatible objects. The adapter object serves the purpose of translating messages between entities that are unable to communicate directly due to incompatible inputs and outputs or other constraints. By translating messages into a readable format for each entity, the adapter can complete the link between the unrelated entities, allowing them to function together successfully in the system. The adapter pattern can be found in many use cases; analog to digital signal converters and memory cards are a few real-world examples of the adapter pattern.
In the context of our mom-and-pop shop, we can create an adapter that will allow customers to pay using a non-default currency. For example, the following code uses USD as the default currency and implements an adapter to convert it to EUR:
The Composite Pattern
Another common structural pattern is the composite pattern. As we move into the realm of complex objects, it is common to design entities that are made up of other defined objects. In many cases, the defined objects and the composed entity share commonalities within their instantiation process, and the composite pattern relies on this relationship to suggest a streamlined design. In the composite pattern, a composite entity comprises one or more leaf elements, all of which extend a base component interface. A few common examples of the composite pattern include an organization’s file-directory structures and employee hierarchies.
To explore this pattern in the setting of our mom-and-pop sandwich shop, we can implement the composite design to define a PhillyCheesesteak sandwich. With the PhillyCheesesteak sandwich acting as the composite entity and each of its ingredients acting as leaf elements, the following example introduces a “Food”base component and implements it across all definitions:
This article discusses how structural patterns extend objects based on their relationships to other entities and how they assist with adopting properties and logic across definitions. In addition, we saw how both the adapter and composite patterns could be applied to a mom-and-pop sandwich shop, with the currency converter implementing the adapter pattern and the PhillyCheesesteak sandwich implementing the composite pattern. Stay tuned for the final post in this series, where we will dive into behavioral design patterns.