Design patterns have two major benefits. First, they provide proven solutions to common development issues. Each solution facilitates the development of highly cohesive modules with minimal coupling. Design patterns make the overall system easier to understand and maintain. Second, they make communication between developers and designers more efficient.
In general, patterns have four essential elements.
The pattern name is a handle developers can use to describe — in a word or two — a design problem, its consequences and its solutions. Naming a pattern immediately increases developers’ design vocabulary, allowing them to design at a higher level of abstraction. Having a vocabulary for patterns also makes it easier to think about designs and to communicate them and their trade-offs to others. However, finding good names is one of the hardest parts of developing the design pattern catalog.
The problem defines the design problem and its context, and describes when to apply the pattern to it. It might describe specific design problems such as how to represent algorithms as objects. It might describe class or object structures that are symptomatic of an inflexible design. Sometimes the problem will include a list of conditions that must be met before it makes sense to apply the pattern.
The solution defines the elements that make up the design: their relationships, responsibilities, and collaborations. The solution doesn’t describe a particular concrete design or implementation, because a pattern is like a template that can be applied in many different situations. Instead, the pattern provides an abstract description of a design problem and how a general arrangement of elements (classes and objects in this case) solves it.
The consequences are the results and trade-offs of applying the pattern. Though consequences are often unvoiced when developers describe design decisions, they are critical for evaluating design alternatives and for understanding the costs and benefits of applying the pattern. The consequences for software often concern space and time trade-offs. They may address language and implementation issues as well. Because reuse is often a factor in object-oriented design, the consequences of a pattern include its impact on a system’s flexibility, extensibility, or portability. Listing these consequences explicitly helps you understand and evaluate them.
Table 1 lists the names and descriptions of a general design pattern template.
Name | Description |
---|---|
Pattern name and classification | A conceptual handle and category for the pattern |
Intent | What problem does the pattern address? |
Also known as | Other common names for the pattern |
Motivation | A scenario that illustrates the problem |
Applicability | In what situation can the pattern be used? |
Structure | Diagram using the Object Modeling Technique (OMT) |
Participants | Class and objects in design |
Collaborations | How classes and objects in the design collaborate |
Consequences | What objectives does the pattern achieve? What are the tradeoffs? |
Implementation | Implementation details to consider, language-specific issues |
Sample code | Sample code in Java and C++ |
Known uses | Examples from the real world |
Related patterns | Comparison and discussion of related patterns |
Table 1. General Design Pattern Template Name and Description |
Table 2 lists the names and descriptions of a Java-specific design pattern template.
Name | Description |
---|---|
Pattern name | The name and a reference to where it was first described |
Synopsis | A very short description of the pattern |
Context | A description of the problem the pattern is intended to solve |
Forces | A description of the considerations that led to the solution |
Solution | A description of the general solution |
Consequences | Implications of using the pattern |
Implementation | Implementation details to consider |
Java API usage | An example from the Java APO (when available) |
Code example | A code example in the Java language |
Related patterns | A list of related patterns |
Table 2. Java Design Pattern Template Name and Description |
The Structure of Java Design Patterns
Design patterns vary in their level of abstraction. There are many design patterns, so it helps to organize them. The sections to follow classify design patterns so that developers can refer to the appropriate patterns. Through this classification, it’s easy to identify as well as incorporate new patterns.
In 1995, software professionals Eric Gamma, Richard Helm, Ralph Johnson and John Vlissides (also known as the Gang of Four or simply GoF) published a book titled Design Patterns. This book is considered the bible or “Gita” of design patterns to the software community at large and it has been very influential in the evolution of design patterns.
Design Patterns described 23 patterns based on the experience of the authors at that time. Today, some 250 or more design patterns are used in the object-oriented world. To organize them, the GoF established three design pattern categories:
- Creational patterns
- Structural patterns
- Behavioral patterns
A creational pattern prescribes the way that objects are created. These patterns are used when a decision must be made at the time a class is instantiated. Typically, the details of the classes that are instantiated — what exactly they are, how and when they are created, and so on — are encapsulated by an abstract superclass and hidden from the client class, which knows only about the abstract class or the interface it implements. The specific type of the concrete class is typically unknown to the client class.
Structural patterns prescribe the organization of classes and objects. These patterns are concerned with how classes inherit from each other or how they are composed from other classes. Common structure patterns include adaptor, proxy and decorator patterns. These patterns are similar in that they introduce a level of indirection between a client class and the class it wants to use. Their intents are different, however.
The adaptor patterns uses indirection to modify the interface of a class to make it easier for a client class to use. The decorator pattern uses indirection to add behavior to a class, without unduly affecting the client class. The proxy pattern uses indirection to transparently provide a stand-in for another class.
Behavioral patterns prescribe the way objects interact with each other. They help make complex behavior manageable by specifying the responsibilities of objects and the ways they communicate with each other.
I further categorize design patterns using two criteria: Scope and Purpose. Scope specifies whether a pattern applies primarily to a class or its objects. Class patterns deal with the relationship between classes and their subclasses. Relationships are initiated through inheritance, so they are fixed at compile time. Objects deal with object relationships, which can change at run time. Almost all patterns use inheritance, so those patterns focus on pure class patterns — patterns I labeled under class.
Purpose explains what a pattern does. Creational patterns concern the process of object creation. Structural patterns deal with the composition of classes or objects. Behavioral patterns characterize the way in which a class or object interacts and is distributed. See Figure 1 for a table of design pattern structures.
Source: Design Patterns: Elements of Reusable Object-Oriented Software |
There are other ways to organize patterns. Some patterns result in similar designs even though they have different intents. You can also pass reference to other patterns to meet your requirements. Figure 2 shows design patterns’ relationship with each other.
Source: Design Patterns: Elements of Reusable Object-Oriented Software |
How Design Patterns Solve Your Problems
Design patterns exist to capture best practices and propagate solutions to commonly recurring problems. The solution lies in establishing the structure and responsibilities of the individual classes and objects that make up the pattern. The main point, however, is that patterns help to identify and isolate system-level variability so the software can evolve over time without adversely affecting the underlying design structure.
The fundamental goals of creating good object-oriented designs are:
- Encapsulate system variability
- Favor composition over inheritance
- Design an interface
How to Select the Right Design Pattern
When a developer decides to use a design pattern to overcome an existing issue, he or she has to choose the kind of design pattern that will fulfill this requirement. The first step is to identify which type of design pattern — creational, structural or behavioral — is going to work well for the problem?
- If you are trying to separate the process of object construction then use creational design patterns such as the singleton, factory, abstract factory and builder patterns.
- If you are trying to interface to a sub-system, connect to an interface your system doesn’t support, or provide a flexible storage structure for adding and manipulating objects, then use structural design patterns such as the bridge, composite and façade patterns.
- If you are trying to access objects, perform operations or interact with operations, use behavioral patterns such as the command, iterator, observer, strategy and visitor patterns.
Now that you have determined which category your problem exists in and you have identified the principle problem you are trying to address, you can begin searching for a pattern that is designed to match your intention.
If you have articulated your intention correctly and cannot find a pattern to match, don’t fret. A design pattern may not be your solution. Design patterns address problems that occur over and over again so that the solutions they offer can be replicated. Your problem may not occur over repeatedly and thus does not have a repeatable solution.
In three upcoming articles, I will go through each design pattern category in detail, including definition, benefits, usability and examples.
Stay tuned.