11/26/12

Chain of responsibility design pattern


The Chain of Responsibility design pattern states:

"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. '

I had an opportunity to implement a slight variation of this pattern on one of my recent coding assignments. The requirements for the task were as follows:

There are two partner sites - Site A and Site B. A user on Site A can become a member of Site B by simply clicking on a link. As soon as the user clicks the link on Site A, the site creates an object that encapsulates all the user's information and passes it on to the code on Site B. The users on both the sites share some common attributes and the code on Site B knows how to retrieve the various attributes from that object and map them to the corresponding attributes on Site B. The end goal is to migrate all the Site A user's information into Site B without the user having to fill out all that information on Site B once again.

Based on the requirements above it felt like a good idea to use the Chain of Responsibility pattern. The incoming object from Site A encapsulates different sets of attributes like the user's personal details, professional details, user's hobbies and interests and other such data. So we had this one big request or object that had to be mapped into multiple different entities on the receiving site. This meant that we had to have multiple handlers(classes) on the receiving side each of which knows how to handle the incoming object and map a certain set of the user's attributes.for e.g. we have a class called PersonalInfoMapper that knows how to map the user's personal details from Site A to Site B. We have a class called ProfessionalInfoMapper that knows how to map the user's professional details and so on.

So we implemented the pattern by developing a set of classes that know how to handle the incoming object. The first class in the flow is provided with the incoming object. It maps the data that it can and passes it on to the next class in line. By the end of the chain of classes, all the data for the user from Site A will have been mapped to the corresponding data on Site B. The code stubs for the pattern are as follows:



//The base classes of all the handlers in the Chain of Responsibility

public abstract class MapperBase
{
 protected MapperBase NextHandler;
 public abstract void Execute(MyType siteAObject);
}

public abstract class MapperBase<T>:MapperBase
{  
 public abstract T MapToModel(MyType siteAObject);
 public abstract void Save(T siteBObject);
 public override void Execute(MyType siteAObject)
        {
   //map the incoming data into a custom object
   var siteBModel = MapToModel(siteAObject);
 
          //save the mapped data
            Save(siteBModel);
 
          //if there is a next handler,continue processing
            if (NextHandler != null)
               NextHandler.Execute(siteAObject);            
        }
}


//The first handler in the Chain
public class PersonalInfoMapper : MapperBase<PersonalInfoModel>
{
 public PersonalInfoMapper(ProfessionalInfoMapper nextHandler)
        {
            NextHandler = nextHandler;
 }
  
       public override PersonalInfoModel MapToModel(MyType siteAObject)
        {
   //map data and return mapped object of type PersonalInfoModel   
 }
  
 public override void Save(PersonalInfoModel model)
        {
  //save data
 }
}


//The Next handler in the Chain
public class ProfessionalInfoMapper : MapperBase<ProfessionalInfoModel>
{
 public ProfessionalInfoMapper(InterestsInfoMapper nextHandler)
        {
            NextHandler = nextHandler;
 }
  
 public override ProfessionalInfoModel MapToModel(MyType siteAObject)
        {
  //map data and return mapped object of type ProfessionalInfoModel   
 }
  
 public override void Save(ProfessionalInfoModel model)
        {
   //save data
 }
}


//The Last handler in the Chain
public class InterestsInfoMapper : MapperBase<InterestsInfoModel>
{
 public InterestsInfoMapper()
        {
            NextHandler = null; //no more handlers in the chain
 }
  
 public override InterestsInfoModel MapToModel(MyType siteAObject)
        {
   //map data and return mapped object of type InterestsInfoModel   
 }
  
 public override void Save(InterestsInfoModel model)
        {
   //save data
 }
}

//Calling Code. The types are being instantiated via unity dependency injection
/* 
   The first handler executes, and if it has a next handler specified it triggers the next handler passing the incoming object to it. This process
   continues until there are no more handlers in the chain
*/
public class CallingCodeClass
{
  [Dependency]
  public PersonalInfoHandler FirstHandler {get;set;}

  public void CallingMethod()
  {
 FirstHandler.Execute();
  }
}

No comments: