In the realm of programming, behavioral patterns are a set of schema that deal with the communication and the interaction between objects in a program. They describe a common solution to the recurring problems in terms of exchange between objects in the context of a program. In other words these patterns help to maintain the code by helping to produce efficient, reusable and modular chunks with different and separate responsibilities.
By reference to the SOLID principles, when writing down a code a programmer has to keep the code plain and simple,so he has to separate the responsibilities and functions of each object in a coherent and constructive manner without breaking up the interaction between them by managing the flow of control between objects, managing the state of objects, and encapsulating complex behavior within objects.
Chain of Responsibility Pattern
The Chain of Responsibility pattern is used to create a chain of objects that can handle requests. Each object in the chain is responsible for handling a specific aspect of the request. If an object cannot handle the request, it passes it on to the next object in the chain. This pattern intends to decouple the communication between a sender and a receiver to achieve a specific action like a check, a conformity validation, security analysis, etc.
A common real-world example of the Chain of Responsibility pattern is a customer service department. In this scenario, customers have different types of requests and complaints, and the customer service department has different levels of support representatives to handle them.
The first level of support representatives may handle basic requests and complaints, such as changing an account password or updating contact information. If the request is more complex, it is passed on to a higher-level support representative who has more knowledge and experience.
If the higher-level support representative is unable to resolve the issue, they may escalate the request to a manager or supervisor. The manager or supervisor is the final level of support, and they have the authority to make decisions and resolve complex issues.
In this example, each level of support representative acts as a handler in the Chain of Responsibility. If a support representative cannot handle the request, it is passed on to the next level until it is resolved.
The Command pattern is used to encapsulate a request as a stand-alone object, this transformation confers to the new objects new functionalities thereby allowing you to parameterize clients with different requests, queue or log requests, and support undoable operations. In this pattern, a command object is created that encapsulates all the information necessary to perform a request.
In the scenario of a restaurant ordering system, the waiter or waitress acts as the “Invoker” and takes orders from customers. These orders are encapsulated as “Command” objects that contain all the necessary information about the order, such as the type of food, quantity, and any special requests.
The “Command” objects are then passed to the kitchen staff, who act as the “Receiver.” The kitchen staff then execute the commands by preparing the food according to the specifications provided in the Command object.
If there is a problem with the order or a mistake was made, the waiter or waitress can undo the command by using the “Undo” method, which will reverse the changes made by the Command object.
The Interpreter pattern is used to define a language and its grammar. It provides a way to interpret sentences in the language and perform actions based on the interpretation. This pattern is useful when you want to define a language and execute actions based on its grammar.
Think of the interpreter of a programming language like python who maps every block of significance to the corresponding instructions, or it may be a calculator that assign a specific meaning to each unit of information defined within it like the addition referred by the symbol “+”.
The Iterator pattern is used to provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation (List, queue, map, etc). This pattern is useful when you want to access the elements of a collection without knowing its underlying implementation.
The Mediator pattern is used to define an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly and allows you to vary their interaction independently. This pattern is useful when you want to limit chaotic communication between objects and define them thereby eliminating any unwanted exchange.
For example, in a chat application, multiple users can send messages to each other in real-time. The mediator pattern can be used to manage the communication between users.
In this case, the mediator object would act as the central point of communication between all the users in the chat room. Whenever a user sends a message, the mediator object would receive it and then forward it to all the other users in the chat room.
The Memento pattern is used to capture and restore an object’s internal state without violating encapsulation. This pattern is useful when you want to restore an object’s state after it has been modified.
An example of this pattern is the “undo” feature in a text editor. When you are working on a document in a text editor, you can use the undo feature to revert the document to a previous state.
The Observer pattern is used to define a one-to-many dependency between objects, so that when one object changes state, all its dependents are notified and updated automatically. This pattern uses the principle of loose coupling ( The relationship between objects should be as minimal and flexible as possible ) by separating the object that produces the events from the observers that receive them. The Subject maintains a list of Observers, and when it changes its state, it notifies all the Observers in the list.
A real implementation of the observer pattern is the news application that notifies its subscribers of the incoming news.
This pattern allows an object to change its behavior as per its internal state variation by encapsulating the object in a wrapper that changes dynamically. This pattern is closely relative to the finite state machine where the set of states are predefined, to and from which the object may morph. The state pattern is particularly useful in situations where an object’s behavior depends on a complex combination of factors that cannot be easily represented by a simple if/else statement.
As a simple example we may draw a parallel to the changing traffic lights.
The Strategy pattern is used to define a family of algorithms, encapsulate each one, and make them interchangeable. This pattern is useful when you want to decouple the algorithms from the clients that use them.
Think of a sorting algorithm that defines an abstract method sort that as its name suggests arranges a given set of values, then implements this method in different manners or strategies (such as bubble sort, insertion sort, and quicksort).
Template Method Pattern
The Template Method pattern is used to define the skeleton of an algorithm in a method, deferring some steps to subclasses to be customized as needed, thereby letting the subclasses redefine certain steps of an algorithm without changing its overall structure.
When creating a program that processes data in a specific order, such as reading input, processing data, and displaying output. The abstract class would define the overall structure of the algorithm, with the concrete classes implementing the specific behavior of each step.
The Visitor pattern is used to define a new operation to a class without changing the class itself. This pattern is useful when you have a complex object structure and want to add new operations or algorithms to that structure without modifying its existing classes. By separating the algorithm or operation from the object structure, you can achieve a high degree of flexibility and maintainability.
behavioral design patterns are reusable solutions to common problems that arise in software development. These patterns describe the interactions between different objects or components in a system, and provide a framework for creating flexible and maintainable code. By using them, developers can improve the reliability and scalability of their code, and reduce the likelihood of errors or bugs. However, it’s important to use these patterns judiciously and not to over-engineer solutions, as this can lead to unnecessarily complex code.