Software design patterns have been around for many years. And as most who follow design patterns know, they gained tremendous traction after Gamma, Helm, Johnson, Vlissides (these gentlemen are fondly known as the Gang of Four (GoF)) released their book Object-Oriented Design Patterns book.
What many don’t know is that design patterns have their roots in architectural patterns. Not architecture as in computer software or computer network architecture design, but architecture as in buildings and towns. Christopher Alexander coined the term “pattern” in the late 1970s in his writings. If you want to get into the heart of the origins of patterns, you can check out his original book The Timeless Way of Building. And his second edition (with additional authors) A Pattern Language: towns, buildings, construction. Although Alexander’s books are interesting from a historical perspective, they will provide little value in your pursuit of software design patterns while taking a large divot from your wallet.
So what are software design patterns? They are solutions to common problems that developers and architects face. Many software design patterns address common situations that developers need to implement in every application they build (e.g. object initialization). Take just about any programming problem and there are only a few good implementation approaches, but many bad approaches. Software design patterns capture the experience of experts in a form that others can reuse.
In the early 1990s, the best source for information on software design patterns was definitely the GoF Object-Oriented Design Patterns book. At my previous employer, we issued the GoF book to all new developers because the book defined a set of well thought through patterns but more importantly defined a new lexicon. The introduction and popularization of the software design pattern lexicon provided a common language for developers to express design options in a very concise manner.
The GoF book was revolutionary because it defined many core pattern names and provided details regarding the intent of the patterns and also included some code snippets to clarify the suggested implementation approach. Unfortunately, the code samples for the patterns are written in C++. Today, with a large portion of business applications being developed in Java, PHP and .NET, you may find the GoF book harder to consume than some more recent pattern books such as Head First Design Patterns for Java and Design Patterns for C# for .NET.
In addition, Martin Fowler’s book Patterns of Enterprise Application Architecture has been very influential. In his book, Fowler identifies new enterprise patterns and provides clarification and enrichment to other lesser-known patterns.
GoF defined four essential elements that are required for any software design pattern:
- The pattern name is a handle we can use to describe a design problem, its solutions, and consequences in a word or two
- The problem describes the context and when to apply the pattern
- The solution describes the elements that make up the design, their relationships, responsibilities and collaborations. The pattern does not describe a concrete solution.
- The consequences are the result and trade-offs of applying the pattern
GoF felt that a problem to be solved by a software design pattern must be seen repeatedly in practice to prove the pattern is applicable beyond the system in which it is first identified.
Patterns are typically divided into 3 categories:
- Creational – like the name suggests, these patterns focus on factories and other techniques that help create (“new”) objects. Although the category sounds fairly straightforward, creational patterns are used in conjunction with dependency injection (sometimes referred to as inversion of control (IoC)) to help avoid unnecessary dependencies in your code which can help, for example, to make unit testing easier (i.e. ability to easily change factory implementation to facilitate stubs or mock objects for testing). The Singleton is an example of popular creational pattern.
- Structural – as their name suggests, these patterns provide structure for one or more classes to achieve a certain objective. It is concerned with how classes are composed to form larger structures or subsystems.
- Behavior – are concerned with the inner workings of classes such as algorithms and the responsibility between classes. Template Method is an example of a popular and powerful behavioral pattern.
Most patterns cannot be read once and completely understood. If you want to be able to use a pattern, you must approach pattern literature with the attitude that you will study the patterns rather than just read them. Some patterns have slight variations between different authors and sometimes slight differences in their sample implementation approaches. Sometimes these differences are a result of different technology platforms and languages. For example, patterns in .NET tend to leverage .NET platform-specific capabilities such as generics and delegates.
However, when learning a new pattern, the single more important take-away is to understand its intent. Otherwise, some patterns start to look alike and many patterns are implemented in a similar fashion by introducing an interface, abstract base class, level of indirection, aggregate object, etc. For example, in .NET the Role Object Pattern (ROP) and State Pattern are similar in their implementation. Both patterns start with a primary class that needs to either have a role or have a state. At first glance, especially by scanning their UML, they appear to be almost the same.
However, after studying their UML more carefully, their implementations are very different – primarily because ROP aggregates one or more roles that are reference by their Role interface and the State Pattern is associated with one state, which is referenced through an abstract base class. But the biggest difference is the intent behind the two patterns – ROP gladly exposes the role(s) from the class and embraces the concept of a role. To the contrary, the State Pattern hides the concept of a state class and just provides a simplified state value to its consumer.
Although patterns are meant to be implementation independent, most texts provide sample implementations. If you do most of your work in one programming language, you will greatly benefit by finding a resource on patterns that has accompanying examples written with your primary language. Even though you might be able to translate say from Java to C# you may miss some platform-specific features in C# during your implementation. For example, some .NET patterns are written to leverage .NET delegate (which are essentially function pointers to simplify threading) and the Observer pattern will leverage the .NET events/event handlers.
In future blogs I will address many GoF software design patterns and provide working samples in C#.