Design patterns in software engineering refers to best practices adapted for particular kind of problems. These patterns have been tried, tested and improved over the years so that to help developers in writing better quality code with minimal efforts. Though these patterns are generalized solutions, these can be adapt to a specific problem. Design patterns are categorized in 3 categories
Let’s see each one of them closely.
Creational design patterns provide solution to instantiate an object in the best possible way for specific situations.
Singleton pattern ensures that there is only one instance of a class created in the Java Virtual Machine. It is used to provide global point of access to the object. In terms of practical use, singleton patterns are used cases like logging, caches, thread pools, configuration settings, device driver objects.
Factory design pattern is used when we have a super class with multiple sub-classes and based on input, we need to return one of the sub-class. This pattern takes out the responsibility of instantiation of a class from client program to the factory class. For Ex: Calendar.getInstance()
It is basically Factory of Factories. In Factory pattern, to create an object you will return object in an if-else block. To remove that we make every component of class as separate module. While creating object, we can pass the modules required for building it. Based on module type, factory will create the object.
When the object contains a lot of attributes, builder pattern solves the issue with large number of optional parameters and inconsistent state by providing a way to build the object step-by-step and provide a method that will actually return the final Object.
Ex: StringBuilder / StringBuffer
It is used when the object creation is costly and requires a lot of time and resources. This pattern provides a mechanism to copy the original object to a new object and then modify it according to our needs. This is achieved by cloning the object. Whether to use shallow or deep copy of the Object properties depends on the requirements.
Structural patterns provide different ways to create a class structure,
It is used to make two unrelated or incompatible entities work together. Like a physical adapter it converts input into a compatible output.
Eg. Arrays.asList(), Collections.list(), InputStreamReader(InputStream)
It is used when we need to create a structure in a way that the objects in the structure has to be treated the same way. Like a picture, which can contain shapes like lines, rectangles, circles etc. however all shapes have common properties and have to be treated same way to make a complete picture.
It is used when we want to provide controlled access of a functionality. Like a wrapper over an existing component which can control or limit the access to certain functionalities.
This pattern is used when we need to create a lot of Objects of a class. Since every object consumes memory space that can be crucial for low memory devices, this pattern can be applied to reduce the load on memory by sharing objects.
Ex: String Pool
Set of Interfaces. Basically used to group similar interfaces so that user only sees one access point to perform different actions on similar objects. Like a data controller can be used expose a connection which can represent a db or a cloud. User can access either of data source from a single interface by passing different parameters, say connection type.
When we have interface hierarchies in both interfaces as well as implementations, then bridge design pattern is used to decouple the interfaces from implementation and hiding the implementation details from the client programs. The implementation of bridge design pattern follows the notion to prefer Composition over inheritance.
Ex: new LinkedHashMap(LinkedHashSet, List)
To be able to modify the properties of an object at run time without impacting other objects of the same type.
Ex – Usually those methods which take the type and return a modified version of the same type. Like in Collections, the checkedXXX(), synchronizedXXX() and unmodifiableXXX() methods.
They provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily.
Template method defines the steps to execute an algorithm and it can provide default implementation that might be common for all or some of the subclasses. Suppose we want to build a house. The steps to build a house are – building foundation, building pillars, building walls and windows. The important point is that we can’t change the order of execution because we can’t build windows before building the foundation. So in this case we can create a template method that will use different methods to build the house.
Ex: basically all behavioral methods that provide a default implementation. Like lifecycle methods of any UI component.
Mediator design pattern is used to provide a centralized communication medium between different objects in a system. If the objects interact with each other directly, the system components are tightly-coupled with each other that makes maintainability cost higher and not flexible to extend easily. Mediator pattern focuses on providing a mediator between objects for communication and help in implementing lose-coupling between objects. Like, Presenter in MVP
Chain of responsibility
Request from client is passed to a chain of objects to process them. Then the object in the chain will decide themselves who will be processing the request and whether the request is required to be sent to the next object in the chain or not. Like exception handling in Java. ATM dispensers work the same way.
This is used when you are interested in the state of an object and want to get notified whenever there is any change. This is basically publish-subscribe mechanism.
This pattern is used when we have multiple algorithm for a specific task and client decides the actual implementation to be used at runtime. One of the best example of this pattern is Collections.sort() method that takes Comparator parameter. Based on the different implementations of Comparator interfaces, the Objects are getting sorted in different ways.
This pattern is used in a request-response model. The request is send to the invoker and invoker passes it to the encapsulated command object. Command object passes the request to the appropriate method of Receiver to perform the specific action. This is very similar to how a runnable object is executed by a thread.
This pattern is used when an Object changes it’s behavior based on it’s internal state. If we have to change the behavior of an object based on it’s state, we can have a state variable in the Object and use if-else condition block to perform different actions based on the state. Using this pattern, we can instead define states of an object. Instead of if-else, different states will perform same action differently. For example, your phone behaves differently when it is on power saving mode.
It is used when we have to perform an operation on a group of similar kind of Objects. With the help of visitor pattern, we can move the operational logic from the objects to another class. Like in a shopping cart, we add items with their price add tax and then show the total. Instead of defining tax / price in the class itself we can define a visitor which will calculate the price and tax on behalf of the item.
The benefit of this pattern is that if the logic of operation changes, then we need to make change only in the visitor implementation rather than doing it in all the item classes.
It is used to define a grammatical representation for a language and provide an interpreter to deal with this grammar. The best example of this pattern is java compiler that interprets the java source code into byte code that is understandable by JVM. Google Translator is also an example of interpreter pattern where the input can be in any language and we can get the output interpreted in another language.
It is used to provide a standard way to traverse through a group of Objects. Like in Java Collection Framework where Iterator interface provides methods for traversing through a collection. We can provide different kind of iterators based on our requirements. It hides the actual implementation of traversal through the collection and client programs just use iterator methods.
Used when we want to save the state of an object so that we can restore later on. Memento pattern is used to implement this in such a way that the saved state data of the object is not accessible outside of the object, this protects the integrity of saved state data.
Ex: Serializable classes