Difference between revisions of "Chain of Responsibility"
(15 intermediate revisions by the same user not shown) | |||
Line 13: | Line 13: | ||
---- | ---- | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==Applicability== | ==Applicability== | ||
Line 40: | Line 26: | ||
* The group of objects that may handle the request must be specified in a dynamic way | * The group of objects that may handle the request must be specified in a dynamic way | ||
+ | |||
+ | |||
---- | ---- | ||
+ | |||
== Drawbacks == | == Drawbacks == | ||
+ | |||
* '''Unhandled requests''' | * '''Unhandled requests''' | ||
**Unfortunately, the Chain doesn't guarantee that every command is handled, which makes the problem worse, since unhandled commands propagate through the full length of the chain, slowing down the application. One way to solve this is by checking if, at the end of the chain, the request has been handled at least once, otherwise we will have to implement handlers for all the possible requests that may appear. | **Unfortunately, the Chain doesn't guarantee that every command is handled, which makes the problem worse, since unhandled commands propagate through the full length of the chain, slowing down the application. One way to solve this is by checking if, at the end of the chain, the request has been handled at least once, otherwise we will have to implement handlers for all the possible requests that may appear. | ||
Line 48: | Line 38: | ||
*'''Broken Chain''' | *'''Broken Chain''' | ||
**Sometimes we could forget to include in the implementation of the handleRequest method the call to the successor, causing a break in the chain. The request isn’t sent forward from the broken link and so it ends up unhandled. | **Sometimes we could forget to include in the implementation of the handleRequest method the call to the successor, causing a break in the chain. The request isn’t sent forward from the broken link and so it ends up unhandled. | ||
+ | |||
+ | |||
+ | ---- | ||
+ | == UML Diagram == | ||
+ | |||
+ | [[Image:CoR_UML.jpg]] | ||
+ | |||
+ | |||
+ | The role of every class: | ||
+ | |||
+ | *'''Handler''' - defines an interface for handling requests | ||
+ | *'''ConcreteHandler:''' | ||
+ | **handles the requests it is responsible for | ||
+ | **If it can handle the request it does so, otherwise it sends the request to its successor | ||
+ | * '''Client''' - sends commands to the first object in the chain that may handle the command | ||
+ | |||
+ | |||
---- | ---- | ||
+ | |||
== Code Examples == | == Code Examples == | ||
+ | |||
+ | ---- | ||
+ | |||
=== Java Example === | === Java Example === | ||
+ | '''Example 1''' -- This code can be found at [http://www.javacamp.org/designPattern/chains.html www.Javacamp.org] | ||
+ | <pre> | ||
+ | import java.io.*; | ||
+ | |||
+ | abstract class PurchasePower { | ||
+ | |||
+ | protected final double base = 500; | ||
+ | protected PurchasePower successor; | ||
+ | |||
+ | public void setSuccessor(PurchasePower successor){ | ||
+ | this.successor = successor; | ||
+ | } | ||
+ | |||
+ | abstract public void processRequest(PurchaseRequest request); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | class Manager extends PurchasePower { | ||
+ | |||
+ | private final double ALLOWABLE = 10 * base; | ||
+ | |||
+ | public void processRequest(PurchaseRequest request ) { | ||
+ | if( request.getAmount() < ALLOWABLE ) | ||
+ | System.out.println("Manager will approve $"+ request.getAmount()); | ||
+ | else | ||
+ | if( successor != null) | ||
+ | successor.processRequest(request); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | class Director extends PurchasePower { | ||
+ | private final double ALLOWABLE = 20 * base; | ||
+ | |||
+ | public void processRequest(PurchaseRequest request ) { | ||
+ | if( request.getAmount() < ALLOWABLE ) | ||
+ | System.out.println("Director will approve $"+ request.getAmount()); | ||
+ | else | ||
+ | if( successor != null) | ||
+ | successor.processRequest(request); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class VicePresident extends PurchasePower { | ||
+ | private final double ALLOWABLE = 40 * base; | ||
+ | |||
+ | public void processRequest(PurchaseRequest request) { | ||
+ | if( request.getAmount() < ALLOWABLE ) | ||
+ | System.out.println("Vice President will approve $" + request.getAmount()); | ||
+ | else | ||
+ | if( successor != null ) | ||
+ | successor.processRequest(request); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class President extends PurchasePower { | ||
+ | private final double ALLOWABLE = 60 * base; | ||
+ | |||
+ | public void processRequest(PurchaseRequest request){ | ||
+ | if( request.getAmount() < ALLOWABLE ) | ||
+ | System.out.println("President will approve $" + request.getAmount()); | ||
+ | else | ||
+ | System.out.println( "Your request for $" + request.getAmount() + " needs a board meeting!"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class PurchaseRequest { | ||
+ | |||
+ | private int number; | ||
+ | private double amount; | ||
+ | private String purpose; | ||
+ | |||
+ | public PurchaseRequest(int number, double amount, String purpose){ | ||
+ | this.number = number; | ||
+ | this.amount = amount; | ||
+ | this.purpose = purpose; | ||
+ | } | ||
+ | |||
+ | public double getAmount() { | ||
+ | return amount; | ||
+ | } | ||
+ | public void setAmount(double amt){ | ||
+ | amount = amt; | ||
+ | } | ||
+ | |||
+ | public String getPurpose() { | ||
+ | return purpose; | ||
+ | } | ||
+ | public void setPurpose(String reason) { | ||
+ | purpose = reason; | ||
+ | } | ||
+ | |||
+ | public int getNumber(){ | ||
+ | return number; | ||
+ | } | ||
+ | public void setNumber(int num) { | ||
+ | number = num; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class CheckAuthority { | ||
+ | public static void main(String[] args) throws Exception{ | ||
+ | Manager manager = new Manager(); | ||
+ | Director director = new Director(); | ||
+ | VicePresident vp = new VicePresident(); | ||
+ | President president = new President(); | ||
+ | manager.setSuccessor(director); | ||
+ | director.setSuccessor(vp); | ||
+ | vp.setSuccessor(president); | ||
+ | |||
+ | //enter ctrl+c to kill. | ||
+ | while (true) { | ||
+ | System.out.println("Enter the amount to check who should approve your expenditure."); | ||
+ | System.out.print(">"); | ||
+ | double d = Double.parseDouble(new BufferedReader(new InputStreamReader(System.in)).readLine()); | ||
+ | manager.processRequest(new PurchaseRequest(0, d, "General")); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | [[Image:javacodeSolution.GIF]] | ||
+ | |||
+ | ---- | ||
=== C# Example === | === C# Example === | ||
+ | '''Example 1''' - This code can be found at [http://www.c-sharpcorner.com/UploadFile/rmcochran/chain_of_command01172007143425PM/chain_of_command.aspx www.c-sharpcorner.com] | ||
+ | |||
+ | Our command will be a PhoneCall class with one method "Converse()" which is our command. | ||
+ | |||
[[Image:c_Caller.GIF]] | [[Image:c_Caller.GIF]] | ||
+ | |||
+ | Command handler will be a person. | ||
[[Image:c_handler.gif]] | [[Image:c_handler.gif]] | ||
[[Image:C_RequestCode.gif]] | [[Image:C_RequestCode.gif]] | ||
+ | |||
+ | This is how we would implement our chain of responsibility: | ||
[[Image:c_main.GIF]] | [[Image:c_main.GIF]] | ||
Line 73: | Line 217: | ||
*Hello. This is ACME Movie Rentals with an important message. | *Hello. This is ACME Movie Rentals with an important message. | ||
+ | ---- | ||
+ | |||
+ | '''Example 2''' - This code can be found at [http://www.dofactory.com/Patterns/PatternChain.aspx#_self1 www.dofactory.com] | ||
+ | <pre> | ||
+ | using System; | ||
+ | |||
+ | namespace DoFactory.GangOfFour.Chain.RealWorld | ||
+ | |||
+ | { | ||
+ | |||
+ | // MainApp test application | ||
+ | |||
+ | class MainApp | ||
+ | { | ||
+ | static void Main() | ||
+ | { | ||
+ | // Setup Chain of Responsibility | ||
+ | Director Larry = new Director(); | ||
+ | VicePresident Sam = new VicePresident(); | ||
+ | President Tammy = new President(); | ||
+ | Larry.SetSuccessor(Sam); | ||
+ | Sam.SetSuccessor(Tammy); | ||
+ | |||
+ | // Generate and process purchase requests | ||
+ | Purchase p = new Purchase(2034, 350.00, "Supplies"); | ||
+ | Larry.ProcessRequest(p); | ||
+ | |||
+ | p = new Purchase(2035, 32590.10, "Project X"); | ||
+ | Larry.ProcessRequest(p); | ||
+ | |||
+ | p = new Purchase(2036, 122100.00, "Project Y"); | ||
+ | Larry.ProcessRequest(p); | ||
+ | |||
+ | // Wait for user | ||
+ | Console.Read(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // "Handler" | ||
+ | |||
+ | abstract class Approver | ||
+ | { | ||
+ | protected Approver successor; | ||
+ | |||
+ | public void SetSuccessor(Approver successor) | ||
+ | { | ||
+ | this.successor = successor; | ||
+ | } | ||
+ | public abstract void ProcessRequest(Purchase purchase); | ||
+ | } | ||
+ | |||
+ | // "ConcreteHandler" | ||
+ | |||
+ | class Director : Approver | ||
+ | { | ||
+ | public override void ProcessRequest(Purchase purchase) | ||
+ | { | ||
+ | if (purchase.Amount < 10000.0) | ||
+ | { | ||
+ | Console.WriteLine("{0} approved request# {1}", | ||
+ | this.GetType().Name, purchase.Number); | ||
+ | } | ||
+ | else if (successor != null) | ||
+ | { | ||
+ | successor.ProcessRequest(purchase); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // "ConcreteHandler" | ||
+ | |||
+ | class VicePresident : Approver | ||
+ | { | ||
+ | public override void ProcessRequest(Purchase purchase) | ||
+ | { | ||
+ | if (purchase.Amount < 25000.0) | ||
+ | { | ||
+ | Console.WriteLine("{0} approved request# {1}", | ||
+ | this.GetType().Name, purchase.Number); | ||
+ | } | ||
+ | else if (successor != null) | ||
+ | { | ||
+ | successor.ProcessRequest(purchase); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // "ConcreteHandler" | ||
+ | |||
+ | class President : Approver | ||
+ | { | ||
+ | public override void ProcessRequest(Purchase purchase) | ||
+ | { | ||
+ | if (purchase.Amount < 100000.0) | ||
+ | { | ||
+ | Console.WriteLine("{0} approved request# {1}", | ||
+ | this.GetType().Name, purchase.Number); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | Console.WriteLine( | ||
+ | "Request# {0} requires an executive meeting!", | ||
+ | purchase.Number); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Request details | ||
+ | |||
+ | class Purchase | ||
+ | { | ||
+ | private int number; | ||
+ | private double amount; | ||
+ | private string purpose; | ||
+ | |||
+ | // Constructor | ||
+ | public Purchase(int number, double amount, string purpose) | ||
+ | { | ||
+ | this.number = number; | ||
+ | this.amount = amount; | ||
+ | this.purpose = purpose; | ||
+ | } | ||
+ | |||
+ | // Properties | ||
+ | public double Amount | ||
+ | { | ||
+ | get{ return amount; } | ||
+ | set{ amount = value; } | ||
+ | } | ||
+ | |||
+ | public string Purpose | ||
+ | { | ||
+ | get{ return purpose; } | ||
+ | set{ purpose = value; } | ||
+ | } | ||
+ | |||
+ | public int Number | ||
+ | { | ||
+ | get{ return number; } | ||
+ | set{ number = value; } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
---- | ---- | ||
− | == | + | == References == |
− | + | # [http://www.javaworld.com/javaworld/jw-08-2003/jw-0829-designpatterns.html Java World] | |
− | + | # [http://www.developer.com/java/other/article.php/631261 Developer.com] | |
+ | # [http://www.oodesign.com/oo_design_patterns/behavioral_patterns/chain_of_responsibility.html OODesign.com] | ||
+ | # [http://codebetter.com/blogs/jeremy.miller/archive/2005/11/06/134359.aspx Codebetter.com] | ||
+ | # [http://www.c-sharpcorner.com/UploadFile/rmcochran/chain_of_command01172007143425PM/chain_of_command.aspx C-Sharpcorner.com] | ||
+ | # [http://www.javacamp.org/designPattern/chains.html Javacamp.org] | ||
---- | ---- | ||
− | --[[User:Djeyarat|Djeyarat]] | + | --[[User:Djeyarat|Djeyarat]] 21:56, 2 April 2007 (EDT) |
Latest revision as of 21:58, 2 April 2007
Contents
Chain of Responsibility (Behavioral Pattern)
- The Chain of Responsibility pattern uses a chain of objects to handle a request, which is typically an event. Objects in the chain forward the request along the chain until one of the objects handles the event. Processing stops after an event is handled.
Purpose
- Is to avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
Applicability
Here are a few situations when using the Chain of Responsibility is more effective:
- More than one object can handle a request
- The handler is not known in advance
- The handler should be determined automatically
- It’s wished that the request is addressed to a group of objects without explicitly specifying its receiver
- The group of objects that may handle the request must be specified in a dynamic way
Drawbacks
- Unhandled requests
- Unfortunately, the Chain doesn't guarantee that every command is handled, which makes the problem worse, since unhandled commands propagate through the full length of the chain, slowing down the application. One way to solve this is by checking if, at the end of the chain, the request has been handled at least once, otherwise we will have to implement handlers for all the possible requests that may appear.
- Broken Chain
- Sometimes we could forget to include in the implementation of the handleRequest method the call to the successor, causing a break in the chain. The request isn’t sent forward from the broken link and so it ends up unhandled.
UML Diagram
The role of every class:
- Handler - defines an interface for handling requests
- ConcreteHandler:
- handles the requests it is responsible for
- If it can handle the request it does so, otherwise it sends the request to its successor
- Client - sends commands to the first object in the chain that may handle the command
Code Examples
Java Example
Example 1 -- This code can be found at www.Javacamp.org
import java.io.*; abstract class PurchasePower { protected final double base = 500; protected PurchasePower successor; public void setSuccessor(PurchasePower successor){ this.successor = successor; } abstract public void processRequest(PurchaseRequest request); } class Manager extends PurchasePower { private final double ALLOWABLE = 10 * base; public void processRequest(PurchaseRequest request ) { if( request.getAmount() < ALLOWABLE ) System.out.println("Manager will approve $"+ request.getAmount()); else if( successor != null) successor.processRequest(request); } } class Director extends PurchasePower { private final double ALLOWABLE = 20 * base; public void processRequest(PurchaseRequest request ) { if( request.getAmount() < ALLOWABLE ) System.out.println("Director will approve $"+ request.getAmount()); else if( successor != null) successor.processRequest(request); } } class VicePresident extends PurchasePower { private final double ALLOWABLE = 40 * base; public void processRequest(PurchaseRequest request) { if( request.getAmount() < ALLOWABLE ) System.out.println("Vice President will approve $" + request.getAmount()); else if( successor != null ) successor.processRequest(request); } } class President extends PurchasePower { private final double ALLOWABLE = 60 * base; public void processRequest(PurchaseRequest request){ if( request.getAmount() < ALLOWABLE ) System.out.println("President will approve $" + request.getAmount()); else System.out.println( "Your request for $" + request.getAmount() + " needs a board meeting!"); } } class PurchaseRequest { private int number; private double amount; private String purpose; public PurchaseRequest(int number, double amount, String purpose){ this.number = number; this.amount = amount; this.purpose = purpose; } public double getAmount() { return amount; } public void setAmount(double amt){ amount = amt; } public String getPurpose() { return purpose; } public void setPurpose(String reason) { purpose = reason; } public int getNumber(){ return number; } public void setNumber(int num) { number = num; } } class CheckAuthority { public static void main(String[] args) throws Exception{ Manager manager = new Manager(); Director director = new Director(); VicePresident vp = new VicePresident(); President president = new President(); manager.setSuccessor(director); director.setSuccessor(vp); vp.setSuccessor(president); //enter ctrl+c to kill. while (true) { System.out.println("Enter the amount to check who should approve your expenditure."); System.out.print(">"); double d = Double.parseDouble(new BufferedReader(new InputStreamReader(System.in)).readLine()); manager.processRequest(new PurchaseRequest(0, d, "General")); } } }
C# Example
Example 1 - This code can be found at www.c-sharpcorner.com
Our command will be a PhoneCall class with one method "Converse()" which is our command.
Command handler will be a person.
This is how we would implement our chain of responsibility:
And this would be re result:
- Father: Hey Mother! Pick up the phone!
- Mother: Hey Son! Pick up the phone!
- Son: Hey Daughter! Pick up the phone!
- Daughter: Hello?
- Hello. This is ACME Movie Rentals with an important message.
Example 2 - This code can be found at www.dofactory.com
using System; namespace DoFactory.GangOfFour.Chain.RealWorld { // MainApp test application class MainApp { static void Main() { // Setup Chain of Responsibility Director Larry = new Director(); VicePresident Sam = new VicePresident(); President Tammy = new President(); Larry.SetSuccessor(Sam); Sam.SetSuccessor(Tammy); // Generate and process purchase requests Purchase p = new Purchase(2034, 350.00, "Supplies"); Larry.ProcessRequest(p); p = new Purchase(2035, 32590.10, "Project X"); Larry.ProcessRequest(p); p = new Purchase(2036, 122100.00, "Project Y"); Larry.ProcessRequest(p); // Wait for user Console.Read(); } } // "Handler" abstract class Approver { protected Approver successor; public void SetSuccessor(Approver successor) { this.successor = successor; } public abstract void ProcessRequest(Purchase purchase); } // "ConcreteHandler" class Director : Approver { public override void ProcessRequest(Purchase purchase) { if (purchase.Amount < 10000.0) { Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number); } else if (successor != null) { successor.ProcessRequest(purchase); } } } // "ConcreteHandler" class VicePresident : Approver { public override void ProcessRequest(Purchase purchase) { if (purchase.Amount < 25000.0) { Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number); } else if (successor != null) { successor.ProcessRequest(purchase); } } } // "ConcreteHandler" class President : Approver { public override void ProcessRequest(Purchase purchase) { if (purchase.Amount < 100000.0) { Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number); } else { Console.WriteLine( "Request# {0} requires an executive meeting!", purchase.Number); } } } // Request details class Purchase { private int number; private double amount; private string purpose; // Constructor public Purchase(int number, double amount, string purpose) { this.number = number; this.amount = amount; this.purpose = purpose; } // Properties public double Amount { get{ return amount; } set{ amount = value; } } public string Purpose { get{ return purpose; } set{ purpose = value; } } public int Number { get{ return number; } set{ number = value; } } } }
References
--Djeyarat 21:56, 2 April 2007 (EDT)