Behavioural Design Patterns: Chain of Responsibility
We had a look on creational design patterns and structural design patterns. On this blog we will focus on behavioural design patterns.
From wikipedia
Behavioural design patterns are design patterns that identify common communication patterns between objects and realise these patterns. By doing so, these patterns increase flexibility in carrying out this communication.
Creational design patterns realise the creation of objects. Structural design patterns provide ways to realise relationships with objects. Behavioural design patterns realise the communication between objects.
The chain of responsibility pattern has to do with command objects being handled and passed to other objects by logic-containing processing objects.
There are many examples that come to mind. One of them is road infrastructure issues which need to be handled by towns, municipalities or states.
The towns should be responsible for its road issues, the municipality should handle the infrastructure that has to do with towns, hamlets and villages and the state all the rest such as urban roads, bridges etc.
There are various road incidents pothole, accidents, the collapse of a bridge you name it. This brings us to the road incident model.
package com.gkatzioura.design.behavioural.cor; public class RoadIncident { private final double latitude; private final double longtitude; private final String report; public RoadIncident(double latitude, double longtitude, String report) { this.latitude = latitude; this.longtitude = longtitude; this.report = report; } public double getLatitude() { return latitude; } public double getLongtitude() { return longtitude; } public String getReport() { return report; } }
The latitude and longitude are the coordinates. Based on the coordinates each geographical region should proceed or pass the incident to the next region.
The next step is implementing the incident handler interface.
package com.gkatzioura.design.behavioural.cor; public interface RoadIncidentHandler { void handle(RoadIncident roadIncident); boolean withinBounds(double lat, double lng); }
Each geographical region should be identify if the incident is within its bound and should be handled by it. If not the incident should be forwarded to its successor.
We should start with the state road incident handler.
package com.gkatzioura.design.behavioural.cor; public class StateRoadRoadIncidentHandler implements RoadIncidentHandler { @Override public void handle(RoadIncident roadIncident) { if(!withinBounds(roadIncident.getLatitude(),roadIncident.getLatitude())) { throw new IllegalArgumentException("Incident cannot be handled by state. No successor available"); } /** * Handle the incident */ } @Override public boolean withinBounds(double lat, double lng) { /** * Reverse geolocation, look up by name, radius based distance etc. */ return true; } }
So the state has no successor. It is the highest level, thus if the incident is out of it’s bounds then it will throw an illegal argument exception.
Then we go to the municipality handler. The municipality handler does have a successor. It can be the state or something else we might come up in the future.
package com.gkatzioura.design.behavioural.cor; public class MunicipalityRoadRoadIncidentHandler implements RoadIncidentHandler { private final RoadIncidentHandler successor; public MunicipalityRoadRoadIncidentHandler(final RoadIncidentHandler successor) { this.successor = successor; } @Override public void handle(RoadIncident roadIncident) { if(!withinBounds(roadIncident.getLatitude(),roadIncident.getLongtitude())) { successor.handle(roadIncident); } else { /** * Handle the incident */ } } @Override public boolean withinBounds(double lat, double lng) { /** * Reverse geolocation, look up by name, radius based distance etc. */ return false; } }
If the municipality incident handler is not able to handle the incident then it will forward it to its successor.
The last one is going to be the town road incident handler.
package com.gkatzioura.design.behavioural.cor; public class TownRoadRoadIncidentHandler implements RoadIncidentHandler { private final RoadIncidentHandler successor; public TownRoadRoadIncidentHandler(final RoadIncidentHandler successor) { this.successor = successor; } @Override public void handle(RoadIncident roadIncident) { if(!withinBounds(roadIncident.getLatitude(),roadIncident.getLongtitude())) { successor.handle(roadIncident); } else { /** * Handle the incident */ } } @Override public boolean withinBounds(double lat, double lng) { /** * Reverse geolocation, look up by name, radius based distance etc. */ return false; } }
The same rule that applies to the municipality applies to the town handler. If the incident is not within its bounds it will forward it to its successor.
Let’s put them all together.
package com.gkatzioura.design.behavioural.cor; public class ChainOfResponsibility { public static void main(String[] args) { StateRoadRoadIncidentHandler state = new StateRoadRoadIncidentHandler(); MunicipalityRoadRoadIncidentHandler municipality = new MunicipalityRoadRoadIncidentHandler(state); TownRoadRoadIncidentHandler firstTown = new TownRoadRoadIncidentHandler(municipality); TownRoadRoadIncidentHandler secondTown = new TownRoadRoadIncidentHandler(state); RoadIncident roadIncident = new RoadIncident(0d,0d,"Something happened"); firstTown.handle(roadIncident); secondTown.handle(roadIncident); } }
So the municipality’s successor is the state. Also the state is the successor for the second town. The first town has the municipality as the successor.
Regarding the first town if the incident can be handled only by the state then it will be forwarded from the first town to the municipality and then from the municipality to the state. Regarding the second town if the incident cannot be handled it will be forwarded to the state.
You can find the sourcecode on github.
Published on Java Code Geeks with permission by Emmanouil Gkatziouras, partner at our JCG program. See the original article here: Behavioural Design Patterns: Chain of Responsibility Opinions expressed by Java Code Geeks contributors are their own. |