Since the late 1980s and the popularity of object-oriented programming languages (one could argue that its precursor is in volumes such as Donald Knuth's Art of Computer Programming, from the 1970s), developers have been keen on deriving the key benefit of object-oriented programming (OOP): reuse. Although OOP goes a long way toward this goal by providing inheritance, encapsulation, and polymorphism, these alone do not produce flexible and reusable software.
In the early 1990s, some software developers drew an analogy with an architect (of actual buildings) named Christopher Alexander, whose books A Pattern Language (1977) and The Timeless Way of Building (1979) postulated and defined recognizable and repeatable patterns that are used in designing great architecture. Simply put, the definition of a pattern encompasses the following description written by Alexander:
Each pattern describes a problem which occurs over and over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it in the same way twice.
These developers immediately realized that the same concepts could be applied to software design and that there likely were recognizable and, therefore, repeatable ways of solving common design problems. The result was the book Design Patterns: Elements of Reusable Object-Oriented Software, by the GoF (Addison Wesley Professional, 1994), which documented 23 such patterns. For framework developers, this book is an invaluable resource to get you thinking and seeing your difficult problems in terms of patterns. Other books, such as Design Patterns Explained: A New Perspective on Object-Oriented Design, by Alan Shalloway and James Trott (Addison Wesley Professional, 2001), help to explain the ideas behind some of the more common patterns more fully and serve as more of a tutorial for developers just getting their minds around patterns. In addition, magazines devoted to framework developers have more recently included regular discussions of patterns; MSDN Magazine has even begun a semi-regular column on the subject.
All design patterns are not created equal, however. In fact, the GoF grouped its 23 patterns into 3 categories, as follows:
Creational. These patterns (and there are five documented in the GoF) deal with the instantiation of objects. Specifically, they are concerned with how to abstract the instantiation process so that software can be independent of who creates the objects, when they are created, and which particular objects (runtime types) are created. Creational patterns often rely on inheritance as well as polymorphism. The goal of creational patterns is to decouple the process of instantiating objects and allowing a more dynamic approach.
Structural. This second grouping (seven documented in the GoF) documents patterns that deal with how objects can be combined to form larger structures. Several of these patterns are typically ones developers are familiar with intuitively, while others are more complex. Structural patterns sometimes use inheritance.
Behavioral. The final grouping of patterns (11 of which are documented in the GoF) include those that are concerned with the communication and assignment of responsibilities between objects. Behavioral patterns can be based on either inheritance (called behavioral class patterns) or aggregation (called behavioral object patterns).
As an example of a pattern, consider the creational Factory Method pattern. This pattern can be used when an application needs to create instances of a class but when the class to create is not known at runtime. The solution, as encapsulated in the pattern, is to defer the instantiation to a factory method.
Each pattern can be documented by a Unified Modeling Language (UML) static structure or class diagram, as shown in Figure 1. In this diagram, you'll notice that the goal is to create objects of the type Product (defined as an interface or, in this case, a base class) such as the derived ConcreteProduct class. This is accomplished as follows: The Creator exposes an abstract FactoryMethod that derived classes such as ConcreteCreator implement, to create instances of a particular ConcreteProduct. The client then manipulates the Creator and might not know at design time which ConcreteCreator was used to create the ConcreteProduct.
Figure 1 The Factory Method pattern.
A simple code example in VB .NET of the pattern is shown here. ConcreteProduct is defined by the Book class, Creator by ProductCreator, and ConcreteCreator by BookCreator:
Public MustInherit Class Product End Class Public Class Book : Inherits Product Public Overrides Function ToString() As String Return "Book" End Function End Class Public MustInherit Class ProductCreator Public MustOverride Function CreateProduct() As Product Public Sub AddToCollection() End Sub End Class Public Class BookCreator : Inherits ProductCreator Public Overrides Function CreateProduct() As Product Return New Book End Function End Class
A client can then write polymorphic code like the following to create a product object and call one of its methods:
Private Sub PrintProduct(ByVal creator As ProductCreator) Dim p As Product = creator.CreateProduct() Console.WriteLine(p.ToString()) End Sub
As a result, the client code needn't be written with a particular product in mind, but can work with any product for which a factory method is in place. Note that the ProductCreator object would still need to be created at some point before passing it into the PrintProduct method. The GoF documents several different ways to implement this.
This example shows how using design patterns can benefit developers in two primary ways:
By providing a common design vocabulary. In his posthumously published work Sylva Sylvarum, Francis Bacon (15611626) identified a mode of human perception that he called "idols of the marketplace," in which ideas or concepts cannot even be conceptualized until an adequate vocabulary is in place. This highlights the primary use of design patterns as spurs to thinking about and solving design issues that we wouldn't even be aware of without the ability to recognize the pattern.
By speeding development and writing less code. Second, and closest to most developer's (or at least their manager's) hearts is the need to speed the development process. By applying patterns, a developer needn't reinvent the wheel, but can instead leverage the effort already undertaken to develop and document the pattern. Many of the patterns also provide optimized solutions, so in many cases, a developer ends up writing less code by applying the pattern.