LasaterConsulting.com

Design Patterns
Type-Object | Properties | Strategy | Entity Relationship | Interpreter | Project Usage | AOM UI

Chain of Responsibility Pattern

Introduction

Developers in .NET sometimes come from scripting language environments that are not strong on Object Oriented Methodologies. OO methodologies like refactoring and using design patterns can be intimidating and the value for the developer is hard to see. What developers new to the OO world, and even more seasoned developers need to understand is that good design is not beyond scope in any project. Good habits simply must be learned that will make the developer a better designer.

In that mind set, I am submitting the fourth of several "how to" real world examples, used in my professional life, on how to use patterns in a simple, easy to follow manner. Many of us have ended up either writing or working on in-line, scripting based code. Taking some ideologies from refactoring methodology, this is not necessarily a bad thing. Of course, when you are first writing an algorithm or a series of logic statements, it seems easier to start with if....then....else. But as you continue to expand this code to more complex forms, this model can quickly become unmanageable. This is where refactoring and design pattern methodology can play an important and useful part in simplifying, enhancing and making code more useable (and understandable) to those who practice good OO design.

This article details a real world example of how to use a Chain of Responsibility pattern to help you deal with creating an object oriented chain of events from inline code.

BackGround

I have used Chain of Responsibility patterns to deal with long if....then....else statements that have successive code written for each path, and each path needs to form a chain. We can make a much more descriptive path by coding it into a framework of successive objects that all keep a reference to one another in the path, so that a logical path is built by the objects themselves.

How to use the code

We start off with looking at the classes before the refactoring effort. The first class we will look at is the Approver class, and it's three inherited classes, Manager, Director and VicePresident. Notice that each is a simple data object and has no idea nor reference to the existence of the other classes.

 		public abstract class Approver
		{
			private string _name;			
						
			public Approver(string name)
			{
				_name = name;				
			}
							
			public string Name
			{
				get{return _name;}
				set{_name = value;}
			}		
			

		}
		
 		public class Manager : Approver
		{
			public Manager(string name) : base(name)
		}
		
		public class Director : Approver
		{
			public Director(string name) : base(name)
		}
		
		public class VicePresident : Approver
		{
			public VicePresident(string name) : base(name)
		}
 
 
 

Next we see the ChangeType enum, and the ChangeRequest class. The ChangeType enum is a simple way to indicate what type of change the request is, the ChangeRequest class is a data object containing the basic request data, including ChangeType. These two pieces of code will not change for this example, and exist solely for aid in describing the pattern.

	
		public enum ChangeType
		{
			Add = 1,
			Modify = 2,
			Remove = 3
		}
					
		public class ChangeRequest
		{
			private int _requestId;
			private ChangeType _changeType;
			private string _changeMessage;
			private bool _isApproved;
			
			public ChangeRequest(int requestId, ChangeType changeType, string changeMessage)
			{
				_requestId = requestId;
				_changeType = changeType;
				_changeMessage = changeMessage;
			}

			public int RequestId
			{
				get{return _requestId;}
				set{_requestId = value;}
			}
						
			public ChangeType TypeOfChange
			{
				get{return _changeType;}
				set{_changeType = value;}
			}
				
			public string ChangeMessage
			{
				get{return _changeMessage;}
				set{_changeMessage = value;}
			}
				
			public bool IsApproved
			{
				get{return _isApproved;}
				set{_isApproved = value;}
			}
		}

Next we see some execution code, in the state it exists in before the refactoring effort. We are creating a ChangeRequest and based on an Approver object (created outside this code block), we allow some measure of processing. This code assumes that several passes may be made into the code block to get different types of approvals. It also does not have the needed chain leading to the next approver. We have no encapsulation of duties, and no way to chain the responsibility between the consecutive approvers.

	
		//approver object is set outside this code example
		ChangeRequest changeRequest = new ChangeRequest(1,ChangeType.Remove,"This is a change.");
		if(approver is Manager)
		{
			if(changeRequest.TypeOfChange.Equals(ChangeType.Add))
				changeRequest.IsApproved = true;
		} 
		else if(approver is Director)
		{
			if(changeRequest.TypeOfChange.Equals(ChangeType.Add) ||
					changeRequest.TypeOfChange.Equals(ChangeType.Modify))			
				changeRequest.IsApproved = true;
		} 
		else if(approver is VicePresident)
		{
			if(changeRequest.TypeOfChange.Equals(ChangeType.Add) ||
					changeRequest.TypeOfChange.Equals(ChangeType.Modify) ||
					changeRequest.TypeOfChange.Equals(ChangeType.Remove))
				changeRequest.IsApproved = true;
						
		} 
	

So the first thing we must do to implement the Chain of Responsibility pattern, is to modify the Approver classes to have an exact idea of the next Approver, and allow the Approver object type to indicate what actions are to be taken. Notice we change the base Approver class and add SetNextApprover and Approve methods. SetNextApprover is the programmatic way we build the chain, and approve is the functional method, containing the functional code for the class.

NOTE: Of course how and where you allow the chained action to be accomplished is more flexible that this example implies. For example, the Approve method could call some function on request or simply be a delegate to another object.

	
		
		public abstract class Approver
		{
			private string _name;
			private Approver _nextApprover;
						
			public Approver(string name)
			{
				_name = name;				
			}
							
			public string Name
			{
				get{return _name;}
				set{_name = value;}
			}
				
			public Approver NextApprover
			{
				get{return _nextApprover;}
			}
			
			public void SetNextApprover(Approver nextApprover)
			{
				_nextApprover = nextApprover;
			}
				
			public abstract void Approve(ref ChangeRequest changeRequest);

		}
		

		public class Manager : Approver
		{
			public Manager(string name) : base(name) {}
		
			public override void Approve(ref ChangeRequest changeRequest)
			{
				//Manager can approve only addition ChangeRequests
				if(changeRequest.TypeOfChange.Equals(ChangeType.Add))
				{
					changeRequest.IsApproved = true;
				} 
				else
				{
					if(NextApprover != null)
						NextApprover.Approve(changeRequest);
				}
				
			}
		}
		
		public class Director : Approver
		{
			public Director(string name) : base(name) {}
		
			public override void Approve(ref ChangeRequest changeRequest)
			{
				//Director can approve only add and modify ChangeRequests not remove
				if(changeRequest.TypeOfChange.Equals(ChangeType.Add) ||
					changeRequest.TypeOfChange.Equals(ChangeType.Modify))
				{
					changeRequest.IsApproved = true;
				} 
				else
				{
					if(NextApprover != null)
						NextApprover.Approve(changeRequest);
				}
				
			}
			
		}
		
		public class VicePresident : Approver
		{
			public VicePresident(string name) : base(name) {}
			public override void Approve(ref ChangeRequest changeRequest)
			{
				//VicePresident can approve only add modify and remove
				if(changeRequest.TypeOfChange.Equals(ChangeType.Add) ||
					changeRequest.TypeOfChange.Equals(ChangeType.Modify) ||
					changeRequest.TypeOfChange.Equals(ChangeType.Remove))
				{
					changeRequest.IsApproved = true;
				}
				
				
			}
		}
		

Now lets look at the execution code. We see the constructors first (which we could further refactor to a factory that returned the chained objects from a organizational table in a database), and next we see how we set up the actual chain of approval events, passing in the consequent approvers to each class. Now when we create and pass the ChangeRequest object into the Manager object we get a chained approval process that is set up in code. For the below example, all three approval chain depths are seen.

	
		Manager manager = new Manager("Joseph");
		Director director = new Director("Thomas");		
		VicePresident vicePresident = new VicePresident("Jason");
		
		manager.SetNextApprover(director);
		director.SetNextApprover(vicePresident);
		
		ChangeRequest changeRequest = new ChangeRequest(1,ChangeType.Add,"This is a change.");
		
		manager.Approve(ref changeRequest); //manager approves

		changeRequest = new ChangeRequest(1,ChangeType.Modify,"This is a change.");
		
		manager.Approve(ref changeRequest); //director approvers

		changeRequest = new ChangeRequest(1,ChangeType.Remove,"This is a change.");
		
		manager.Approve(ref changeRequest);//VP approves

Points of Interest

This is the fourth installment in the series I am writing on real world design patterns. All examples and the bulk of this article are taken from my professional experience as an architect. The examples given are templates only, and the designer must keep in mind that they are the ones who must decide where different patterns, if any, may be best used in their code.

Deciding to perform a refactoring effort from existing code to a pattern must be weighed on the necessity and need of the code itself. Patterns are only design templates, helpers to accommodate better overall design. I might stress that making the effort to use patterns will strengthen your overall design ability, but like your basic coding skills, it is something learned and cultivated.

If this or any other in this series on design patterns is helpful or you have questions or comments please e-mail me at chris.lasater@gmail.com.

History

This is the first revision and is the fourth installment in a series.

Related Articles

Other articles in this series include:

  1. Bridge & Adapter Pattern
  2. Chain Of Responsibility Pattern
  3. Facade Pattern
  4. Factory & Strategy Pattern
  5. Mediator Pattern
| Make a Donation to Help Build/Maintain our site! |©2004 Lasater Consulting

1