In software engineering, a design pattern is a repeatable solution to a commonly occurring problem in software design. Christopher Alexander, a civil engineer, is the one who came up with the idea of design patterns. While working on designing buildings and towns, he found that there are common design problems and certain design constructs can be used repeatedly to solve them. He documented and published his experiences on design patterns.
Around three decades ago, software professionals began to incorporate Alexander’s design principles. This early work led others to write on design patterns in software development. In 1995, Eric Gamma, Richard Helm, Ralph Johnson, and John Vlissides published their book Design Patterns: Elements of Reusable Object Oriented Software . This book became the most popular book on design patterns. The design patterns in this book are also called as “Gang of Four” (GOF) as there were four authors. These design patterns are still relevant and widely used. GOF design patterns are based on Object Oriented programming and are language agnostic. This blog post describes the purpose of all 23 GOF design patterns.
Along with GOF, there are other design patterns which are also used in software design. There are technology specific design patterns such as Core Java EE design patterns . There are design patterns that solve design problems in a particular type of application such as Workflow patterns . There are concurrency patterns  to address design issues in multithreaded applications, Enterprise Integration Patterns  that help in designing integrated applications. There are architecture patterns that help us in designing robust enterprise applications . There are also design patterns for SOAP and RESTful web services .
The design patterns also provide a vocabulary for communication and documentation of software designs.
Object Oriented Programming and Domain Modeling
Object oriented programming has helped in making code more maintainable with features such as encapsulation and inheritance as compared to procedural programming. A class defines attributes and behavior related to an entity, thus providing encapsulation or a boundary. Objects enable domain modeling.
A domain is a collection of objects and their relationships. If entities of an application are designed using domain modelling , they enable to define entities and their relationships. The domain models can be used to document complex domains.
Java Persistance API (JPA), a Java technology that maps objects to relational database and provides abstraction over the JDBC, uses domain modeling to define entities and their relationships.
SOLID design principles by Robert Martin are the object oriented principles that help to write easily readable and maintainable code. SOLID is an acronym that stands for S – Single Responsibility principle, O – Open Closed principle, L – Liskov Substitution principle, I – Interface Segregation principle and D – Dependency Injection.
Single Responsibility principle
This design principle (S in SOLID) states that a class should have only one reason to change. A class should have only one responsibility and there should be one class for one responsibility. Classes that adhere to this principle have high cohesion and are more maintainable then classes that don’t support this principle.
For example, if you have to parse different message formats then create a separate class for parsing each format instead of writing code in a single class. If a message format changes, then only the respective class will change. Classes for other formats will not change and they don’t need to be tested.
Composite design pattern which is a GOF design pattern uses Single Responsibility design principle.
Open Closed principle
The Open Closed design principle (the O in SOLID) states that a class should be open for extension but closed for modification. This principle restricts addition of new behavior in existing class hierarchy but allows extending the hierarchy by addition of subclasses. The Decorator and Strategy GOF design patterns follow Open Closed design principle. Decorator allows extension of behavior during runtime whereas Strategy allows implementation of behavior during compile time.
Dependency Inversion principle/Dependency Injection/The Hollywood principle
Dependency Inversion principle states that instead of instantiating referenced classes within a class, they should be instantiated outside the class definition and passed as references in the class. Dependency Inversion (D in SOLID) injects the reference of the class instance through setter methods of interface or abstract class type. Dependency Inversion relies on abstraction instead of concrete classes. This design principle promotes loose coupling. Since the objects are instantiated outside the class and not within the class this is also called as Inversion of Control.
As Dependency Inversion relies on abstraction, any concrete class instance of the abstract class type can be used. Since the objects are instantiated outside the referenced classes, changes from one object to another of same abstract class type can be done easily without changing the referenced classes. Also, this code change is done at one place instead of making change in every referenced class. This is the advantage of using Dependency Inversion.
The terms Inversion of Control, Dependency Injection, Dependency Inversion mean the same. The term Hollywood principle (don’t call us, we’ll call you) also means the same.
Spring framework provides Dependency Injection.
EJB 3.1 uses both Context and Dependency Injection.
The Factory Method, Template Method, Observer and Chain of Responsibility GOF design patterns follow Dependency Inversion principle.
The Principle of Least Knowledge
While designing the implementation of a system you should avoid multiple classes referring to each other. This makes the application difficult to maintain as changes in one part of code could cascade to multiple class changes. Also, unit testing of tightly coupled classes is difficult.
The principle of least knowledge states that a class should only interact with few closely associated classes. It promotes loose coupling and makes the code resilient to changes. It states that only the following methods should be called:
- The ones within the object
- The ones that belong to arguments of a method in the object
- Methods of objects created within a method
- Methods of instance variables
Facade which is a GOF design pattern follows this principle.
Apart from design patterns and design principles, a programming language can have efficient ways of writing code. For example, in the book Effective Java , Joshua Bloch has written about efficient ways of writing a Java code.
GOF design patterns
GOF design patterns are divided into three categories – Creational, Structural and Behavioral.
1. Creational design patterns
1.1. Factory Method
The most commonly used GOF Creational design pattern is Factory Method. This design pattern takes the object creation logic outside the client and encapsulates it in a class often called as a Factory. A Factory class provides a static method to create and return objects. The return type of this method is either an abstract class or an interface. Factory Method is used when you do not want to give fine grained control of directly accessing the implementation classes to the client. Factory method decouples the client code from the actual implementation class as it uses abstraction. This design pattern provides Dependency Inversion.
Factory method can use Singleton design pattern to avoid creation of new objects each time the factory method is called.
DocumentBuilderFactory class of Java is an example of Factory method implementation. Another example of Factory method implementation is the getConnection method of DriverManager class in JDBC. The return type of this method is an interface java.sql.Connection. The actual object returned by this method is of specific driver class type.
Singleton is a GOF creational pattern used to create only one object of a class. The object is created within the class and its constructors are declared private to ensure that the class cannot be instantiated from elsewhere outside the class definition. If a singleton object is being deserialized, then it should return same singleton instance.
With the introduction of enum in Java SE 5, Singleton objects can be declared as enum instead of class.
Enterprise Java Beans (EJB) 3.1 provides Singleton stateless session beans. Spring framework also provides singleton beans.
The use of singleton is discouraged in multithreaded applications as it slows down the performance.
Builder is a GOF creational design pattern. It is used to create a complex object which could consist of any of its components.
StringBuilder class in Java is an example of builder design pattern. A StringBuilder allows creation of a String object which is a combination of multiple strings without creating a new String object for every concatenation.
Another example of Builder implementation – you need to create a SQL query with dynamic Order By clause. There are multiple sorting criteria and the order by should only contain selected criteria. The Builder class in this design has a method buildClause that generates Order By clause based on the selected criteria. All criteria are defined as instance variables in Builder class. If none of the criteria are set the buildClause returns empty string otherwise an Order By clause followed with column names.
Prototype is a creational GOF design pattern used to create multiple complex objects with some initial state. Instead of creating and initializing complex objects every time they are needed, this design pattern allows creating clone of an existing complex object. Nested objects within the cloned object can be shallow or deep cloned.
For example, if you need to create a copy of a complex object then creating a new instance of all related objects and initializing state of each object could become expensive. In a business automation project which allows marketing materials such as documents, questionnaires etc. to be reused, the existing workflow instance can be cloned instead of creating a new workflow.
1.5. Abstract Factory
This creational GOF design pattern provides one layer of abstraction above factory method. The abstract factory is used to decide which factory class should be used to create objects.
2. Structural design patterns
Composite design pattern allows defining part-whole hierarchy of objects. If a complex object constitutes of another object of its own type or another object of similar type, it can be defined using composite pattern.
For example, a workflow application allows reviewers to review a project or a program by filling a questionnaire. A program can consist of multiple projects or multiple sub programs. A project is always a part of program. You can design this by creating two classes Project and Program implementing Reviewable interface. The Program class uses a “has a” relationship to declare variable of interface type Reviewable. The instance of Reviewable can either be of class Project or Program. The Questionnaire class also has “has a” relationship of type Reviewable. Due to the interface Reviewable, client class Questionnaire can deal with project and program uniformly.
Composite is based on Single Responsibility design principle.
Adapter design pattern converts interface of a class into the interface that client expects. Adapter classes provide connectivity between the client and classes with incompatible interfaces. The classes with incompatible interfaces are also called as Adaptee classes. Adapter classes either translates a method call from a client into the method calls that the Adaptee class supports. An Adapter can also transform the input or output objects into the object types supported by the Adaptee class.
Adapter classes allow the client code to be decoupled from the Adaptee classes where neither of them knows about the other. The Adapter also helps to handle the changes in the API being called and the client code without changes on either side.
Facade provides a unified interface to components encapsulating the low level component calls. Façade provides loose coupling between the calling component and the subsystems by encapsulating multiple subsystem calls. Façade decouples the client from low level granular calls. This is the most widely used structural design pattern.
The difference between Façade and an Adapter is that the former provides a simple interface to multiple components whereas the other allows calling a component with incompatible interface.
Façade follows the principle of least knowledge. If the façade has to access other objects which do not fall under this principle, it is advised to create multiple facades.
Bridge decouples an abstraction from its implementation by using class hierarchies. It helps to combine two subsystems in such a way that either of the subsystem can be modified without affecting the other.
Bridge pattern helps to add a refined abstraction or concrete implementation during application enhancement.
In the example given in Composite pattern above, if you want to allow reviewers to give feedback either in a predefined questionnaire or as comments, you’ll have to define an abstraction class called Feedback. Questionnaire and Comment classes are subclasses of Feedback. The Feedback class will have variable of type Reviewable. In this way Bridge design pattern decouples both subsystems – Product/Program and Questionnaire/Comment.
Decorator helps to extend functionality of an object dynamically.
The difference between Builder and a Decorator design pattern is that the former adds the functionality during compile time of the class whereas the latter adds it dynamically. This design pattern avoids subclassing by allowing to dynamically extend the behavior.
The best example of decorator pattern in Java is the AWT Adapter classes which are extended to add custom functionality when a certain event occurs.
Another example of Decorator implementation in Java I/O is the FileInputStream instance which is enclosed inside a BufferedReader instance. The former allows reading bytes from file. The latter extends the behaviour dynamically to enable reading one line at a time.
Decorator follows open closed principle.
This design pattern promotes reuse of objects thus reducing memory usage and improving performance. The implementation of object pools such as database connection pools, EJB stateless and message driven beans pools in an application server is an example of Flyweight design pattern.
Proxy provides a placeholder for an object to control access to it. This design pattern provides a level of indirection by hiding the actual component for complexity reduction.
The best example of proxy implementation is the remote method calls (RMI). The clients only have access to the stub whereas the actual business implementation resides on the server. This way the business logic is hidden from the client.
Proxy provides resource pooling and security.
EJB also uses a proxy design pattern for handling remote method calls.
The difference between proxy and façade is that the latter provides a unified access to a set of subsystem calls whereas in case of the former the method call on stub or proxy object is propagated to the same method of the actual object. In case of proxy the signature of actual method being called is exposed to the client whereas in façade the methods of subsystems being called are hidden from client.
3. Behavioral Design Patterns
This behavioral design pattern allows defining a family of algorithms and makes them interchangeable. It enables an algorithm to be selected at runtime.
For example, if there are various payment message types such as pain.001.001.03, SAPIDOC, pain.008.001.02 etc. and you have to implement an algorithm to parse a message in any of these formats and transform it into a uniform data model, then you’ll have to define an interface MessageConverter which declares the algorithm’s behavior such as parseMessage and convertToCanonical. Since the message formats can be XML or fixed length and their structure is also different, there will be implementation classes for each message format that implement the interface. The client code which calls this parser can decide which strategy class to be called depending on the message type.
The interface that defines algorithm’s behavior helps to create family of algorithm.
This design pattern promotes Open Closed design principle. In the above example, you can go on adding as many classes that implement the MessageConverter you wish for different message formats but their behavior parseMessage and convertToCanonical cannot change.
Factory Method is often used along with Strategy to instantiate Strategy class.
3.2. Template Method
Template Method design pattern defines a skeleton of an algorithm allowing some behavior to be deferred to the subclasses. This structural GOF design pattern allows subclasses to define certain behavior while keeping the algorithm defined by superclass intact. The super class declares abstract methods which are called by template method. The template method is also declared in the super class. The abstract methods act as placeholders in template method. The actual implementation of these methods is defined by the subclass.
For example, a class PDFReader reads PDF documents such as telephone bill or credit card bill of a consumer. This class is used in an application that allows comparison of two credit card or telephone bills. The algorithm for reading a bill is same for both types of bills. However, the bill type will determine the contents within the bill. So, in PDFReader class there is a template method readBill which calls methods convertToText and parseBill. The convertToText is a generic operation for any bill type. It will convert PDF to text. The parseBill method will take the text output of the first method but steps to parse the bill depend on bill type. The parseBill is abstract method in PDFReader. TelephoneBillReader and CreditCardBillReader are subclasses of PDFReader and both define the concrete method for parseBill. If the PDF document is a telephone bill then the parseBill method of TelephoneBillReader is called.
3.3. Chain of responsibility
Chain of responsibility allows multiple objects to handle a request. It helps you to define an execution flow with multiple steps. A class exists for each step. The next step to be called is defined outside the class. This design pattern provides you the flexibility to change the execution flow when required without any change in the class. The sequence of classes to be executed in a flow is defined externally.
This design pattern is implemented by having all classes in the execution flow implement an interface which defines a method that is executed when each class is called. This method also allows input parameters to be passed to each step and also share objects in an execution flow. A method to configure the next class is also defined in the interface.
This design pattern uses Dependency Inversion principle. It decouples sender of a request from its receiver. It simplifies and makes execution flow more maintainable, as next class to be called in execution chain is defined outside the class. It allows changes to be made to the execution flow dynamically.
Command is a behavioral design pattern that encapsulates method calls in an object called as command object. The command object encapsulates the method and class name to be called along with its parameters. There is another invoker class which invokes the command object. The invoker does not know about anything that is defined in the command. The invoker and command objects are decoupled from each other.
The difference between Chain of responsibility and Command pattern is that the latter is used when you don’t need to call multiple methods in a chain.
Queues can also be used in the implementation of Command design pattern.
Struts framework uses Command design pattern.
Java Messaging (JMS) is based on Command Message pattern similar to Command pattern. Command Message is an Enterprise Integration Pattern.
Iterator is a behavioral design pattern used to access elements of an aggregate object sequentially without exposing its underlying structure. The aggregate object acts as a container which stores objects.
Java provides Iterator interface to iterate through all objects stored in a collection (instances of classes that implement Collection interface). The Iterator is an example of this design pattern.
Mediator is a GOF behavioral pattern that provides an object to handle interaction between multiple objects in a uniform way. This design pattern promotes loose coupling by handling interactions among several objects centrally instead of each object having to communicate directly with the other. By decoupling them, it increases their reusability and maintainability.
For example, if a system has multiple input and output formats and you need to transform any input to any output format, then direct transformation from one format to another will require handling of multiple input to output format transformations. If you have to add a new input format then you will have to manage its transformation to every output format. Such a system would become very complex and difficult to maintain as the count of input and output formats grows. Mediator design pattern reduces the complexity by transforming all input formats to a unified data model and from the unified data model to all output formats.
Memento design pattern allows undo or rollback on an object such that it is returned to its previous state.
Both EJB (Stateful session beans) and Spring allow an object to be rolled back to its previous state.
Observer is a behavioral design pattern that helps in creating one to many dependency between objects in such a way that if one object changes state all its dependents are notified about the change. The dependent objects need to be registered with the object being observed to get notifications. The dependent objects are called as observers.
This design pattern helps in implementing publish subscribe model without tight coupling.
Java provides Observable class and Observer interface to implement publish subscribe model. The object being observed for state changes should be a subclass of Observable. The setChanged method is used to indicate that the state of the object is changed. The notifyObserver method updates the observer about state change. The observer should be registered first using addObserver method of Observable class to get the notification.
Observer follows Dependency Inversion principle.
This behavioral GOF design pattern allows implementing a state machine where the object behaves in a different way depending on its state.
For example, a domain object can have two states read only and read write. In read write state you can call its setter methods to set its attributes. In read only state you can only call getter methods.
State is implemented using a context object which is an object whose state is changed. A context has a state which is of abstract class type. The states that the context can have are the subclasses of the state abstract class. The context object is shared with all states. Each concrete state defines relevant behavior and when the method of state is called, it calls the method of context object.
In Strategy the client selects the behavior. In state design pattern the behavior is constant for a state. It changes with change in the state of context object. The difference in both is that in state the client does not know which behavior is called whereas in strategy the client defines which behavior should be called. In state, the behavior is encapsulated and delegation is used to decide which behavior to use.
Visitor helps to add a capability to heterogeneous or composite set of objects externally. This design pattern uses Iterator to iterate through the objects and call a behavior which is specific to each object type.
The objects of heterogeneous type allow access to their state through a method which allows access to an object of interface type. This interface is generally named Visitor or Visitable. An implementation of this interface for each object type is defined which defines external behavior. When the object type Visitor implementations are called, they in turn call the methods of the object. In this way this design pattern uses a double dispatch mechanism.
This design pattern is best used to define an operation which has to iterate through multiple objects. For example, calculating the total cost for multiple items of different types unrelated to each other where the calculation algorithm might change and should reside outside these objects.
Interpreter is a GOF behavioral design pattern used to interpret words and expressions in a language.
The following table gives the list of 23 Gang of Four Design Patterns discussed in this blog post.
|Factory Method||Adapter||Chain of Responsibility|
Table 1. List of Gang of Four Design Patterns
Further Reading and References
- Design Patterns – Eric Gamma, Richard Helm, Ralph Johnson, John Vlissides
- Core J2EE Design Patterns – Dan Malks, Deepak Alur, and John Crupi
- Pattern-Oriented Software Architecture: Patterns for Concurrent and Networked Objects –Douglas C. Schmidt
- Enterprise Integration Patterns – Gregor Hohpe
- Patterns of Enterprise Application Architecture – Martin Fowler
- Service Design Patterns – Rob Daigneau
- Domain-driven design – Eric J. Evans
- Effective Java – Joshua Bloch
- Head First Design Patterns – Elisabeth Freeman