Loose Coupling with Chain of Responsibility
Let’s talk about one of the Behavioral design patterns, the Chain of Responsibility pattern. The main goal of the Chain of Responsibility pattern is to have loose coupling in the application design. In this pattern, when the sender sends a request, that request will go through a chain of multiple objects and those objects will try to handle the request.
Think of the chairperson, secretary, and committee member. They have different types of permission due to their post. So, if you want to design a system based on their permission hierarchy, you can use this design pattern easily.
Did you know that Java Logger uses the Chain of Responsibility pattern?. Let's try the Logger class to see an example. Logger class has several levels such as
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
(SEVERE is the highest and FINEST is the lowest).
Now in here, I have set the Level to INFO
. Look at the output. It will print all the log messages I have set.
Output:
May 26, 2021 4:34:47 PM loger.LoggerExample main
INFO: Info levelMay 26, 2021 4:34:47 PM loger.LoggerExample main
WARNING: Warning levelMay 26, 2021 4:34:47 PM loger.LoggerExample main
SEVERE: Severe level
Now let's set the level to WARNING
and you will see, it prints the warning and severe both messages.
May 26, 2021 4:35:49 PM loger.LoggerExample main
WARNING: Warning levelMay 26, 2021 4:35:50 PM loger.LoggerExample main
SEVERE: Severe level
Accordingly, if you set the level to SEVERE
, it will only print SEVERE
level
May 26, 2021 4:36:54 PM loger.LoggerExample main
SEVERE: Severe level
Let me show you how the Chain of Responsibility is implemented.
To implement the Chain of responsibility pattern, we need a Handler class, ConcreteHandler classes and a Client class.
Handler:
The handler class could be declared as an Interface or Abstract. Also, it's the Superclass for all ConcreteHandler classes.
ConcreteHandler:
These are the classes that handle the request. If a ConcreteHandler can’t handle the request. it forwards the request to the next successor ( next ConcreteHandler ). Unlike the Handler, Abstract or Interface, these classes have the code to handle the request.
Client: Pass request to the chain of objects.
Now let's to an example. Remember “John Bee” from the “Introduction to Prototype Design Pattern” example, Since John Bee has a drone delivery system, now he wants to give his customers “Black Friday” discounts for their purchases.
10% off for every purchase more than 250$, 30% off of every purchase more than 500$ purchase and 50% from purchases more than 1000$.
First, let's create the order details class.
Next, as in the UML diagram let's first, create the Handler class. This could be an Interface or an Abstract class. Since the Handler should know about the Successor, let's create a Handler object known as successor
and implement a Setter method called setSuccessor()
and getDiscount()
method for it.
Now that's down let's go to create the ConcreteHandler classes that handle the request (The discount classes that calculates the discounts).
Now let's create multiple orders and see how this design pattern handles them.
Total price of products: 250.0 USD
Discount: 10%
Discount price: 225.0 USDTotal price of products: 450.0 USD
Discount: 10%
Discount price: 405.0 USDTotal price of products: 500.0 USD
Discount: 30%
Discount price: 350.0 USDTotal price of products: 600.0 USD
Discount: 30%
Discount price: 420.0 USDTotal price of products: 1000.0 USD
Discount: 50%
Discount price: 500.0 USDTotal price of products: 1200.0 USD
Discount: 50%
Discount price: 600.0 USD
As you can see it works fine and you a decoupled code. Also, to start the chain I have implemented a class named Discount. This is not related to the design pattern and it's only used to pass the successor. I have done this because just in case if the “FirstDicount” was changed.
Also remember that the client can send the request to any handler, not only to the first one and the client doesn't know which handler handles the request. If you are looking for the full implementation, please visit my Github profile. I have uploaded it here.