Introduction to Design Patterns
Within each engineering discipline, engineers have found that when analysing problems, similar types of sub-problems would tend to recur from time to time. Having had experience from previous project work, they knew that a similar solutions to those problems could be reused. This became the basis for design patterns.
The term pattern was first coined by Christopher Alexander (Alexander, Ishikawa, Silverstein, Jacobson, Fiksdahl-King & Angel. 1977), while writing about architectural patterns within buildings and towns. Similarly, patterns have emerged as engineers have gained more experience with object oriented design methodologies.
A design pattern is essentially a reusable design template which has been developed as a response to a recurring problem within system design. As systems are analysed, similar subsystems are designed for managing data, sharing it among the subsystems, and ensuring it’s safety and integrity. Since the subsystems within different systems can be broadly similar, then the design approaches to them can also be broadly similar.
Gamma, Helm, Johnson & Vlissides (1995) describe a design pattern as:
A design pattern names, abstracts, and identifies the key aspects of a common design structure that make it useful for creating a reusable object-oriented design. The design pattern identifies the participating classes and instances, their roles and collaborations, and the distribution of responsibilities, Each design pattern focuses on a particular object-oriented design problem or issue. It describes when it applies, whether it can be applied in view of other design constraints, and the consequences and tradeoffs of its use.
Design patterns have several aspects. Firstly, a meaningful name can help designers define what the pattern is trying to achieve, such as Proxy, or Facade. Once a pattern has a name, it becomes part of the designers vocabulary, and allows system design to proceed at a somewhat higher level of abstraction.
Patterns apply to a particular problem, so that problem is usually documented by the pattern. Designers considering the use of a pattern should pay close attention to the problem being solved by the pattern, and the similarity to their own problems.
A pattern describes a solution to a problem. This is usually expressed as a UML diagram or fragment. Patterns often make use of several collaborating classes, abstract classes and interfaces. The relationships between these are documented by the pattern description, allowing for replication of the pattern by designers.
Finally, a design pattern should document its consequences. As with any design choice, there are implementation and run-time consequences and tradeoffs to be considered. Design patterns are somewhat generic solutions to common problems, so a good understanding of how they could affect the functional and non-functional requirements, and performance constraints of the system being designed is important.
Similarly, if the problem domain is simplistic, then the use of a design pattern could be overkill. For example, the State (Gamma, Helm, Johnson & Vlissides. 1995) design pattern provides a flexible way to implement a state machine. If the problem domain consists of very few states, then the use of a pattern like this may be considered too much.
Anybody who has used the Java Collections API will be intimately familiar with the Iterator design pattern, as it is used extensively. Gama, Helm, Johnson & Vlissides (1995) define the Iterator as:
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
The Java Collections API is a set of interfaces and implementing classes which provide a rich set of containers. These containers are used to store collections of objects. The top level interfaces define Lists, Sets, Maps, and Queues. The implementing classes provide concrete implementations for these, and combinations of these interfaces, such as to create containers such as SortedSets, LinkedLists, and TreeMaps.
Regardless of the specifics of how each container organises the data it contains, a typical application will at some point want to gain access to the contents of a collection in a sequential fashion. The Iterator pattern provides a consistent way to enable this across the various collection classes.
Each collection class implements the Collection interface, which in turn implements the Iterable interface. This specifies the iterator() operation, which returns a class implementing the Iterator interface.
The Iterator interface then defines several methods for manipulating the contents of a collection. These are (Sun Microsystems, 2006):
- boolean hasNext()
- Object next()
- void remove()
By using the Iterator interface, multiple collection classes can expose a consistent means by which the elements of the collection can be accessed, in a flexible manner. The Iterator pattern shifts the responsibility for access to a collection away from the collection itself, and on to an associated iterator class.
This has the effect of increasing flexibility of choice of collection class and reducing coupling between collections and their clients. Different collection implementations can be easily substituted without large scale disruption to the client code.
Patterns in System Design
Blaha and Rumbaugh (2005) note of design patterns:
A pattern is a proven solution to a general problem. Various patterns target different phases of the software development life cycle. There are patterns for analysis, architecture, design, and implementation. You can achieve reuse by using existing patterns, rather than reinventing solutions from scratch. A pattern comes with guidelines on when to use it, as well as trade-offs in its use.
Patterns can be applied at many stages of the software life cycle. For example, if a project is being developed using the Unified Process, then design patterns become an important consideration during the elaboration and perhaps construction phases of the project. If a project is being developed using an agile methodology, then design patterns are typically introduced as code is refactored after new features are added.
It is important to be aware of design patterns, and know when it is appropriate to use them. Overuse of design patterns can result in increased code complexity. Sommerlad (2007) argues against overuse of design patterns, and using design patterns without thinking, stating:
Patterns should make you think, but the Gang of Four design patterns often allow developers to avoid thinking about design because implementing patterns is so easy, especially Singleton. And getting rid of them is so hard. This asymmetry between thought and implementation ease leads to design-pattern accumulations in systems, even when they’re not needed and get in the way of improvement or simplicity. I’ve seen people refactoring a simple “if” statement into State, thus increasing code size and complexity for no reason.
Nobel (2007) states that design patterns have emerged from “an understanding of past designs, past practice, which designs have succeeded, which designs have failed, and why”. As such design patterns offer system designers solutions to common design problems, and a way to express designs in a widely understood manner. Nobel (2007) goes on to state that:
For example, Iterator, Observer, and Composite are all built into the designs of industry-standard libraries, and you can’t be a competent programmer without understanding them
It is clear that design patterns represent an evolution in how designers approach system design. Design patterns have expanded our vocabulary, and offer many benefits if used appropriately. They have become another tool in the designers toolbox, and their use will continue to grow and evolve, as we continually update patterns with our growing experience.
- Alexander, C., Ishikawa, S., Silverstien, M., Jacobson, M., Fiksdahl-King, I. & Angel, S. (1977) A Pattern Language. Oxford University Press: New York.
- Blaha, M. & Rumbaugh, J. (2005) Object-Oriented Modeling and Design with UML. Pearson Prentice Hall.
- Gamma, E., Helm, R., Johnson, R. & Vlissides J. (1995) Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
- Nobel, J. (2007) Point/Counterpoint. IEEE Software. Vol. 24, Issue 4. 68 – 71.
- Sommerlad, P. (2007) Point/Counterpoint. IEEE Software. Vol. 24, Issue 4. 68 – 71.
- Sun Microsystems. (2006) Interface Iterator. Retrieved on June 12, 2008 from http://java.sun.com/javase/6/docs/api/java/util/Iterator.html.