A software design pattern is an optimized, repeatable solution to a commonly occurring problem in software engineering. It is not a finished design, class or library that can be plugged into your code directly. Rather, it is a template for solving a problem that can be used in many different situations.
Design patterns are not language specific either. Good design patterns are implementable in most programming languages, depending on the capabilities of the language of course. They also speed up the development process by providing tested and proven development paradigms.
Effective software design requires considering issues that may not become visible until later in the implementation process. Reusing design patterns helps to prevent subtle issues that can cause major problems later on. It also improves code readability for programmers and architects familiar with the patterns. In addition, patterns allow developers to communicate using well-known, well understood names for software interactions.
Correctly using a design pattern is very important. Implement it in the wrong way and it can create more problems than it solves.
A building architect will also use proven principles to solve issues that arise while designing a building. For example by applying a certain brick-pattern to a wall or pavement he may solidify its structure. It's easier to communicate the name of that pattern to the contractor constructing the building, than to go over its design every time this pattern is required.
This article sets the basis for understanding design patterns and presents an introduction to the world of software design patterns by giving a brief description of all 23 Gang of Four patterns.
The Gang of Four
As Boromir points out correctly, it's uncommon to give an introduction to design patterns and not explain who the Gang of Four are.
The Gang of Four are the authors of the book, "Design Patterns: Elements of Reusable Object-Oriented Software": Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides.
When the book was published in 1994, it initiated the concept of design patterns in software development. This influential book describes various development techniques and pitfalls in addition to providing 23 object-oriented programming design patterns. The patterns described in this book are therefore often called 'Gang of Four' (or GoF) design patterns.
Nowadays there are more design patterns than just the GoF patterns. However, these 23 patterns are still valid, are often used and understanding these will provide a solid basis to your general design pattern knowledge. Therefore, this article is scoped to giving a high-level description of these 23 patterns.
Design pattern categories
The Gang of four design patterns are divided into 3 fundamental groups:
- Creational Patterns provide ways to instantiate single objects or groups of related objects.
- Abstract Factory Creates an instance of several families of classes
- Builder Separates object construction from its representation
- Factory Method Creates an instance of several derived classes
- Prototype A fully initialized instance to be copied or cloned
- Singleton A class of which only a single instance can exist
- Structural Patterns provide a manner to define relationships between classes or objects.
- Adapter Match interfaces of different classes
- Bridge Separates an object’s interface from its implementation
- Composite A tree structure of simple and composite objects
- Decorator Add responsibilities to objects dynamically
- Facade A single class that represents an entire subsystem
- Flyweight A fine-grained instance used for efficient sharing
- Proxy An object representing another object
- Behavioral Patterns define manners of communication between classes and objects.
- Chain of Responsibility A way of passing a request between a chain of objects
- Command Encapsulate a command request as an object
- Interpreter A way to include language elements in a program
- Iterator Sequentially access the elements of a collection
- Mediator Defines simplified communication between classes
- Memento Capture and restore an object's internal state
- Observer A way of notifying change to a number of classes
- State Alter an object's behavior when its state changes
- Strategy Encapsulates an algorithm inside a class
- Template Method Defer the exact steps of an algorithm to a subclass
- Visitor Defines a new operation to a class without change
For each of these categories, the design patterns belonging to that category will be introduced in the next chapters.
Creational design patterns are patterns that deal with object creation mechanisms. They try to create objects in a manner that is suitable to the situation. Basic forms of object creation may result in design problems or added complexity to the design. Creational design patterns solve this problem by controlling the object creation in a certain way and hiding the creation logic.
The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme, without specifying their concrete classes
A factory of factories, or "super factory" so to speak.
The client code calls creational methods of a factory object instead of creating products directly with a constructor call (
new operator). Since a factory corresponds to a single products variant, all its products will be compatible.
Client code works with factories and products only through their abstract interfaces and it does not need to know which exact concrete objects it gets from each of these internal factories. This allows the same client code work with different products. You just create a new concrete factory class and pass it to client code.
Separate the construction of a complex object from its representation so that the same construction process can create different representations
In other words, it defines an instance for creating an object but lets subclasses decide which class to instantiate. Newly created objects are referred to through a common interface.
The more complex an application is, the complexity of classes and objects used increases. Complex objects are made of parts produced by other objects that need special care when being built. An application might need a mechanism for building complex objects, that is independent from the ones that make up the object as a whole. This is the problem that can be solved by using the Builder design pattern.
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses
Creating an object often requires complex processes that are not appropriate to include within a composing object. Several problems can occur when the creating the object. The object's creation may:
- lead to duplication of code.
- require information not accessible to the composing object.
- provide an insufficient level of abstraction.
- not be part of the composing object's concerns.
The factory method design pattern handles the above problems by defining a separate method for creating the objects, which subclasses can then override to specify the derived type of product that will be created. This design pattern relies on inheritance, since object creation is delegated to subclasses that implement the factory method to create objects.
Factory method versus Abstract factory
The main difference between these two design patterns, is that the factory method is a single method whereas the abstract factory is an object. In the Factory Method design pattern, the client expects an implementation of an interface or abstract class, but doesn't know exactly what the concrete class the factory will return. With the Abstract Factory pattern there is an extra level of abstraction. The client does not even know what factory it's going to use. First it gets a Factory and then it calls a Factory method.
Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype
The prototype pattern is used to instantiate a new object by copying all of the properties of an existing object and thus creating an independent clone. This is particularly useful when the construction of a new object is inefficient or expensive.
Ensure a class has only one instance and provide a global point of access to it
The singleton pattern ensures that only one object of a particular class is ever created. All further references to objects of the singleton class refer to the same underlying instance.
There are some who are critical of the singleton pattern and consider it to be an anti-pattern:
- It is frequently used in scenarios where it is not beneficial
- It can introduce unnecessary restrictions in situations where a sole instance of a class is not actually required.
- It introduces global state into an application.
These design patterns are all about class and object composition and provide a manner to define relationships between classes or objects. Structural class-creation patterns use inheritance to compose interfaces, while structural object-patterns define ways to compose objects to obtain new functionality.
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces
The adapter pattern is used to provide a link between two otherwise incompatible types by wrapping the "adaptee" with a class that supports the interface required by the client. This pattern involves a single class which is responsible to join functionalities of independent or incompatible interfaces.
A real life example could be a case of card reader which acts as an adapter between memory card and a laptop. You plugin the memory card into the card reader and the card reader into the laptop so that memory card can be read via the laptop.
Decouple an abstraction from its implementation so that the two can vary independently
When an abstraction can have one of several possible implementations, the usual way to accommodate them is to use inheritance. An abstract class defines the interface to the abstraction, and concrete subclasses implement it in different ways. However, this approach isn't always flexible enough. Inheritance binds an implementation to the abstraction permanently, which makes it difficult to modify, extend, or reuse abstractions and implementations independently.
The bridge pattern involves an interface that acts as a bridge, which makes the functionality of concrete classes independent from interface implementer-classes. Both types of classes can be altered structurally without affecting each other.
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly
The composite pattern describes a group of objects that is treated the same way as a single instance of the same type of object. The intent of a composite is to compose objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.
Composite should be used when clients ignore the difference between compositions of objects and individual objects. If programmers find that they are using multiple objects in the same way, and often have nearly identical code to handle each of them, then composite is a good choice.
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality
The decorator pattern is used to extend or alter the functionality of objects at run-time. This is done by wrapping them in an object of a decorator class without affecting the behavior of other objects from the same class. This provides a flexible alternative to using inheritance to modify behavior.
This pattern is often useful for following the practices of the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
An example of an application implementing a decorator pattern would be a graphical user interface toolkit. This toolkit should let you add properties like borders or behaviors like scrolling to any user interface component.
Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use
The Facade design pattern is often used when a system is very complex or difficult to understand because the system has a large number of interdependent classes or its source code is unavailable. This pattern hides the complexities of the larger system and provides a simpler interface to the client.
This pattern typically involves a single wrapper class that contains a set of members required by client. These members access the system on behalf of the facade client and hide the implementation details.
Use sharing to support large numbers of fine-grained objects efficiently
The flyweight pattern is used to reduce the memory and resource usage for complex models containing many hundreds, thousands or hundreds of thousands of similar objects. It is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. Often some parts of the object state can be shared, and it is common practice to hold them in external data structures and pass them to the objects temporarily when they are used.
A classic example usage of the flyweight pattern is the data structures for graphical representation of characters in a word processor. It might be desirable to have, for each character in a document, a glyph object containing its font outline, font metrics, and other formatting data, but this would amount to hundreds or thousands of bytes for each character. Instead, for every character there might be a reference to a flyweight glyph object shared by every instance of the same character in the document. Only the position of each character in the document would need to be stored internally.
Provide a surrogate or placeholder for another object to control access to it
The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. The proxy provides the same public interface as the underlying subject class, adding a level of indirection by accepting requests from a client object and passing these to the real subject object as necessary.
A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate. In short, a proxy is an agent object or wrapper that is being called by the client to access the real serving object behind the scenes.
Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy, extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked. For the client, usage of a proxy object is similar to using the real object, because both implement the same interface.
Behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication.
[Chain of Responsibility]
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it
In other words, the chain of responsibility pattern is used to process varied requests, each of which may be dealt with by a different handler.
The pattern consists of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle, while the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.
In a variation of the standard chain-of-responsibility model, some handlers may act as dispatchers, capable of sending commands out in a variety of directions, forming a tree of responsibility. In some cases, this can occur recursively, with processing objects calling higher-up processing objects with commands that attempt to solve some smaller part of the problem. In this case recursion continues until the command is processed, or the entire tree has been explored. An XML interpreter might work in this manner.
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations
The command pattern is used to express a request, including the call to be made and all of its required parameters, in a command object. The command may then be executed immediately or held for later use.
Four terms always associated with the command pattern are command, receiver, invoker and client:
- A command object knows about the receiver and invokes a method of the receiver. Values for parameters of the receiver method are stored in the command, the receiver object that will execute these methods is also stored in the command object by aggregation.
- The receiver then does the work when the execute-method of the command is called. An invoker object knows how to execute a command, and optionally does bookkeeping about the command execution.
- The invoker does not know anything about a concrete command, it knows only about command interface. Invoker object(s), command objects and receiver objects are held by a client object.
- The client decides which receiver objects it assigns to the command objects, and which commands it assigns to the invoker. The client also decides which commands to execute at which points. To execute a command, it passes the command object to the invoker object.
Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing, without the need to know the class of the method or the method parameters. Using an invoker object allows bookkeeping about command executions to be conveniently performed, as well as implementing different modes for commands, which are managed by the invoker object, without the need for the client to be aware of the existence of bookkeeping or modes.
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language
The interpreter pattern is a design pattern that specifies how to evaluate sentences in a language.
The basic idea is to have a class for each symbol in a specialized language. The syntax tree of a sentence in the language is an instance of the composite pattern and is used to interpret the sentence for a client.
An example of an interpreter would be the Reverse Polish Notation. This is a mathematical notation in which every operator follows all of its operands. In this notation
1 + ((2 + 3) × 4) − 5 can be written as
1 2 3 + 4 × + 5 −
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation
The iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers. In some cases, algorithms are necessarily container-specific and thus cannot be decoupled.
In C#, a typical implementation of an iterator pattern would be the implementations of the
IEnumerator/IEnumerator<T> interfaces that provides a means to iterate over some items.
Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently
Programs can be made up of a large number of classes among which logic and computation is distributed. However, as more classes are added to a program, the problem of communication between these classes may become more complex. This makes the program harder to read and maintain. Additionally, it can become difficult to change the program, since any change may affect code in several other classes.
With the mediator pattern, communication between objects is encapsulated within a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby reducing coupling.
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later
The memento pattern is used to capture the current state of an object and store it in such a manner that it can be restored at a later time without breaking the rules of encapsulation (undo via rollback).
The memento pattern is implemented with three objects: the originator, a caretaker and a memento:
- The originator is some object that has an internal state.
- The caretaker is going to do something to the originator, but wants to be able to undo the change.
The caretaker first asks the originator for a memento object. Then it does whatever operation (or sequence of operations) it was going to do. To roll back to the state before the operations, it returns the memento object to the originator. The memento object itself is an opaque object (one which the caretaker cannot, or should not, change).
Typical examples of memento patterns are saving and restoring the state of a player in a computer game or the implementation of an undo operation.
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically
The observer pattern is used to allow an object to publish changes to its state. Other objects subscribe to be immediately notified of any changes. In this pattern an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
It is mainly used to implement distributed event handling systems, in "event driven" software, which is built in in most modern languages for easy programming and short code.
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class
The state pattern is used to alter the behavior of an object as its internal state changes. The pattern allows the class for an object to apparently change at run-time.
The state design pattern implements a state machine by implementing each individual state as a derived class of the state pattern interface. State transitions are then implemented by invoking methods defined by the pattern's superclass.
It can be interpreted as a strategy pattern that is able to switch the current strategy through invocations of methods defined in the pattern's interface.
Using the state design pattern can be a cleaner way for an object to change its behavior at runtime without the need for large monolithic conditional statements and thus improve maintainability.
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it
The Strategy Design Pattern can be used when you want to perform a function, but you might use different techniques. It also lets the algorithm vary independently from clients that use it.
For example, a class that performs validation on incoming data may use the Strategy pattern to select a validation algorithm depending on the type of data, the source of the data, user choice, or other discriminating factors. These factors are not known until run-time and may require radically different validation to be performed.
The essential requirement to implement the Strategy pattern is the ability to store a reference to some code in a data structure and retrieve it. This can be achieved by mechanisms such as the native function pointer, the first-class function, classes or class instances in object-oriented programming languages, or accessing the language implementation's internal storage of code via reflection. One of the ways to do this in .NET, is to make use of the Action<T>-class.
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure
In the template method of this design pattern, one or more algorithm steps can be overridden by subclasses to allow differing behaviors while ensuring that the overarching algorithm is still followed.
A concrete class is created that provides the steps of an algorithm design. Steps that are considered invariant (never changing) are implemented inside the base class. The steps that are considered to be variant, are given a default implementation or none at all. These variant steps must be supplied by the concrete derived subclasses. This way the general algorithm is saved in one place but the concrete steps may be changed by the subclasses.
The template method pattern occurs naturally in C# where polymorphic or abstract methods are called. An example would be a generated class in which the base functionality is implemented and abstract methods are included and left for subclasses to implement.
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates
The visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this is the ability to add new operations to existing object structures without modifying them. It is one way to follow the open / closed principle.
Basically, the visitor allows adding new virtual functions to a family of classes, without modifying the classes. Instead, a visitor class is created that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch.
An real-world example of this pattern would be the operation of a taxi company. When a person calls a taxi company (accepting a visitor), the company dispatches a cab to the customer. Upon entering the taxi the customer, or Visitor, is no longer in control of his or her own transportation, the taxi is.