Today, in this blog post, I am going to discuss about the chain of responsibility pattern. As the name suggests, the pattern deals with a chain of objects where each object is aware of the next object that needs to be called.
Lets see where this pattern can be used.
- When your request contains data that needs to be processed by different objects where each object deals with different parts of the requests.
- When your request contains data that needs to be processed at several levels and processing can be halted at any of the levels.
- When your request needs to be modified before reaching its final destination.
Lets elaborate on the concept using an example.
Scenario :
I will try to explain this pattern as well by taking an example of a game as I have been doing in my previous posts. Lets assume that we have a game with two players. Each player can attack the other player. When a playerq attacks player2 and vice versa, the victim player's health is reduced. Now the game isin't much fun until you have some extra features like a 'Shield' to protect the players.
Now suppose player1 has a shield that can absorb 30 percent of the damage of the attack. And player 2 has a Shield that can absorb 70 percent of the damage of the attack. But when a player attacks another player, how will the damage be absorbed by the shield??
So, here are our requirements.
- A player is not aware of the properties of the shield that protects him.
- A player is not aware of the number of shields that protect him.
- A player is not aware of the shields that protect the other player.
- A shield can be attached to any player.
- A shield absorbs the damage to the level set by its configuration.
- A shield is not aware whether the next object that will absorb the remaining damage is a shield or a player.
So, we have a candidate for the Chain of responsibility pattern.
Using the chain of responsibility pattern to solve the scenario
In this scenario, in order to implement the chain of responsibility pattern, we are going to create an interface called 'Damageable'. The interface declares a function - int absorb(int damageValue). The purpose of the function is to absorb a damage and return the damage that could not be absorbed. Both our Player class and our Shield class implement this interface and hence both of them are Damageable objects. The player class saves as its instance variable, the next damageable object to which it can pass on the damage before it can process the damage itself. The shild object also stores a similar variable in its instance variable the informs it of the next object in the chain that can absorb the damage. In the 'absorb' method of the Player class, the player checks if there is any damageable object in its chain that can process the damage. If there is such an object, the player delegates the process of absorbing the damage to the next damageable object in the chain. This continues until the chain returns with the actual value of the damage that can be processed by the player, for which the player calls the 'reduceHealth' method.
The following code example demonstrates an implementation of the above scenario using the chain of responsibility pattern in Java.
Damageable.java
public interface Damageable { //Returns the damageValue that it could not absorb. public int absorb(int damageValue); }
Player.java
public class Player implements Damageable{ private int health; private Damageable nextDamageableObject; private String name; public Player() { health=100; } public int reduceHealth(int value) { System.out.println("Health Before Reducing : "+ health); if(value<=health) { health-=value; value=0; } System.out.println("Health After Reducing : "+ health); return value; } public int getHealth() { return health; } public void setHealth(int health) { this.health = health; } public void setNextDamageableObject(Damageable nextDamageableObject) { this.nextDamageableObject = nextDamageableObject; } public Damageable getNextDamageableObject() { return nextDamageableObject; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int absorb(int damageValue) { if(nextDamageableObject!=null) { damageValue = nextDamageableObject.absorb(damageValue); } //After all the damageable objects have absorbed the damage, //reduce the player's health by the specified amount System.out.println("Finaly Attempting To Reduce "+ this.getName() + "'s health by "+damageValue); return reduceHealth(damageValue); } public void attack(Player p,int damageValue) { p.absorb(damageValue); } }Shield.java
public class Shield implements Damageable{ private int damageAbsorbPercent; private Damageable nextDamageableObject; private String name; public Shield(int damageAbsorbPercent, Damageable nextDamageableObject, String name) { this.damageAbsorbPercent = damageAbsorbPercent; this.nextDamageableObject = nextDamageableObject; this.name = name; } public int getDamageAbsorbPercent() { throw new UnsupportedOperationException("Not supported yet."); } public Damageable getNextDamageableObject() { return nextDamageableObject; } public void setNextDamageableObject(Damageable nextDamageableObject) { this.nextDamageableObject = nextDamageableObject; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int absorb(int damageValue) { int absobedValue = (damageValue* damageAbsorbPercent)/100; damageValue = damageValue-absobedValue; System.out.println(this.name + " absorbed damage % (" + damageAbsorbPercent +")"); //The shield passes on the damage to the next damageable object in the chain. if(nextDamageableObject!=null) { damageValue = nextDamageableObject.absorb(damageValue); } return damageValue; } }Game.java
public class Game { private Player player1; private Player player2; public Game() { } public Player getPlayer1() { return player1; } public void setPlayer1(Player player1) { this.player1 = player1; } public Player getPlayer2() { return player2; } public void setPlayer2(Player player2) { this.player2 = player2; } }Main.java
public class Main { public static void main(String[] args) { Game game=new Game(); Player player1 = new Player(); Player player2 = new Player(); player1.setName("Player 1"); player2.setName("Player 2"); Shield s1=new Shield(30, null, "Normal Shield1"); Shield s2=new Shield(70, null, "Power Shield"); player1.setNextDamageableObject(s1); player2.setNextDamageableObject(s2); player1.attack(player2, 30); System.out.println("\n"); player2.attack(player1, 30); //Now add another shiled to the player1 Shield s3=new Shield(30, player1.getNextDamageableObject(), "Normal Shield2"); player1.setNextDamageableObject(s3); System.out.println("\nAdded Another Shiled to player1 which absorbs 30 percent damage"); player1.attack(player2, 30); System.out.println("\n"); player2.attack(player1, 30); } }
Following is the output that I get on my console when I run the main program.
Power Shield absorbed damage % (70) Finaly Attempting To Reduce Player 2's health by 9 Health Before Reducing : 100 Health After Reducing : 91 Normal Shield1 absorbed damage % (30) Finaly Attempting To Reduce Player 1's health by 21 Health Before Reducing : 100 Health After Reducing : 79 Added Another Shiled to player1 which absorbs 30 percent damage Power Shield absorbed damage % (70) Finaly Attempting To Reduce Player 2's health by 9 Health Before Reducing : 91 Health After Reducing : 82 Normal Shield2 absorbed damage % (30) Normal Shield1 absorbed damage % (30) Finaly Attempting To Reduce Player 1's health by 15 Health Before Reducing : 79 Health After Reducing : 64
Signing Off
Ryan