Design patterns can be termed as well-documented solutions to the most commonly occurring problems in software engineering. It turns quite hectic for developers to bang out their heads on the problem that someone else has already solved. Every developer aspires to write industrial-level code that is easily applicable to a different problem, be maintainable, and be more readable, and reusable. It needs a proper code structuring mechanism or pattern to be taken into account, and therefore it becomes crucial to solving this challenge. This is where design patterns play an important role by providing ground to these common problems arising in a particular circumstance.
The design patterns are needed because they don't change over time and can be kept into practice with standard or high levels of applications.
Benefits of Design Patterns
They are the best solutions: Design patterns are frequently used by all the developers because they know that it would work. Not only that, they are well aware of the design patterns because they certainly have been revised multiple times before they have been implemented.
They are reusable: Design patterns portray a reusable solution that can be modified under certain circumstances to solve multiple problems, as they cannot exist only to a single problem.
They are expressive: Design patterns need no further explanation because they can explain the large problem to bits and pieces quite efficiently.
They enhance communication: Developers being familiar with design patterns can set common goals to a problem which helps them to communicate with each other about the potential threats and solutions to those problems.
No code refactoring: Design patterns are often termed as optimal solutions for various problems. If an application is being written keeping design patterns in mind, it is assumed that the generated solution is the only efficient solution and hence needs no code refactoring.
They lower codebase size: Being the only optimal solution, design patterns preserve precious space by implementing a required solution in a few lines of code with very little footprint.
We would now be talking about these three design patterns in detail using suitable examples.
Creational Design Patterns
The above design patterns are all about the instantiation of a class. It can be elaborated further by the division of creating class and object creational patterns. This design pattern deals with the creation of mechanisms that optimize class-object complexities into simple and easy patterns. Complex design patterns need a high approach and time and this problem can be tackled by creational design patterns by controlling the class to inherit effectively and object to use delegation wisely to get the stuff done. Some of the popular design patterns are already shown in the image above. Let's discuss some of the categories.
Mercedes gives 20.8 kmpl!
In this example, we have defined a class/function Automobile with attributes brand and mileage. The getDetails() method used in the example will print the given brand and mileage of the automobile in the format shown in the output above. We have instantiated an object for the function/class by provoking the constructor method using new keyword for the given attributes.
In this example, we have implemented the use of a prototype cheetah, which is later cloned to create a new object with the Object.create method by creating a constant cheetah1 in which the name of the Trainer will be stored according to the standard ES6 process.
Structural Design Patterns
This design pattern is all about class and object composition or their relationship. It has structurally defined mechanisms for class and objects to ensure that if the part of a system changes, the entire system doesn't rely upon it and change. It is ensured by figuring out the class-creation patterns to use inheritance to compose interfaces and object-creation patterns to define a way to roll out new functionality. It also consists of various categories which are already shown in the image above. Let's discuss some of them.
Adapter Design Pattern
The adaptor design pattern is mainly used for different designs of objects to work together in a single interface. Consider that we are building a library that accepts structures and formats in JSON. We need to have a legacy API to respond in the XML format and this response is later used to generate charts but charts accept only JSON objects. Therefore, an adapter function is written to convert this XML to JSON as and when required. This adapter function would let us connect two different forms of objects. See the below image for reference.
Composite Design Pattern
It is one of the most used structural design patterns where we compose objects into tree structures and then these structural trees are treated as individual objects. Consider a jQuery-based example of how the composite design pattern is structured.
In the above example, jQuery makes it quite easy to apply different methods on the selected DOM nodes by easily accessing combinations. The method addClass is used to hide the details of implementation. iIt mainly ensures that a group of objects behave as they are individual objects.
Behavioral Design Patterns
This design pattern relies upon the pattern of recognizing, implementing, and communicating between disparate objects. Behavioral patterns are mostly concerned with communication because they help to ensure the disparate parts have proper synchronization of information. Thus, it improves the flexibility of communication.
Let's discuss some categories of a behavioral design pattern.
Chain of Responsibility
This design pattern is mainly used to construct a system where each request passes through a chain of events and is handled by handlers. The requests are processed and passed from one chain to another or are simply rejected. This design pattern is optimal for a system that uses sequential checks for processing requests. Let's take an example of an ATM. Whenever we request a withdrawal amount from ATM, the machine processes the request and dispenses out the amount with a series of combinational notes (Rs.500. Rs. 200, Rs.100).
In this coding example, a request object is created whenever we request an amount to be withdrawn. This provokes a series of calls for the object where it is chained together and event handlers handle particular denominations. Finally, the ATM can dispense the requested combination of notes which satisfies the processed request.
The observer pattern is a behavioral design pattern whose main task is to define a subscription mechanism to notify multiple observers or objects about any occurring event. This alerts event handlers to carry out event-driven responses. This pattern is popularly called Pub/Sub that abbreviates for Publication/ Subamount followed by a series of one-to-one dependency amongst objects. This design pattern is used to promote a great object-oriented design and enhance loose coupling.
In this example, we have constructed a notification service system whose task is to notify the user about the subscriptions. In this system, we have created multiple object and event listeners for listening updates which are called subscribers. Therefore, we have defined two classes here namely Subject and Observer. These two classes are needed to hold critical information along with the list of observers. Whenever the critical information state changes, the subject would notify all of its observers about the event using notify method shown in the code above. The observer class invokes an update method that calls notification requests from the subject.