The Chain of Responsibility (CoR) pattern is a behavioral design pattern that allows an object to pass a request along a chain of potential handlers until the request is handled. This pattern promotes loose coupling by enabling the sender of a request to not know which object will handle it, allowing for dynamic and flexible request handling.
Why Use the Chain of Responsibility Pattern?
- Decoupling Sender and Receiver: The sender of a request does not need to know which handler will process it.
- Flexibility: Handlers can be added or removed dynamically.
- Separation of Concerns: Different concerns or steps in request processing are encapsulated in separate handler classes.
- Responsibility Sharing: Multiple objects can handle the same request, providing redundancy and fault tolerance.
Real-World Example
Imagine a customer service system where customer requests (like complaints, inquiries, or feedback) need to be processed. These requests can be handled by different departments such as Customer Support, Technical Support, and Management, depending on the nature of the request. The Chain of Responsibility pattern can be used to implement this system.
Implementation in C#
Let's implement a simple customer service request handling system using the Chain of Responsibility pattern in C#.
Step 1. Define the Request
First, we define the request class that contains the details of the customer request.
public class CustomerRequest
{
public string RequestType { get; set; }
public string Description { get; set; }
public CustomerRequest(string requestType, string description)
{
RequestType = requestType;
Description = description;
}
}
Step 2. Define the Handler Interface
Next, we define an abstract handler class with a method to handle the request and a reference to the next handler in the chain.
public abstract class RequestHandler
{
protected RequestHandler NextHandler;
public void SetNextHandler(RequestHandler nextHandler)
{
NextHandler = nextHandler;
}
public abstract void HandleRequest(CustomerRequest request);
}
Step 3. Implement Concrete Handlers
We create concrete handlers for different types of requests. Each handler will process the request if it can, or pass it to the next handler in the chain if it cannot.
public class CustomerSupportHandler : RequestHandler
{
public override void HandleRequest(CustomerRequest request)
{
if (request.RequestType == "Customer Support")
{
Console.WriteLine($"Customer Support: Handling request - {request.Description}");
}
else if (NextHandler != null)
{
NextHandler.HandleRequest(request);
}
}
}
public class TechnicalSupportHandler : RequestHandler
{
public override void HandleRequest(CustomerRequest request)
{
if (request.RequestType == "Technical Support")
{
Console.WriteLine($"Technical Support: Handling request - {request.Description}");
}
else if (NextHandler != null)
{
NextHandler.HandleRequest(request);
}
}
}
public class ManagementHandler : RequestHandler
{
public override void HandleRequest(CustomerRequest request)
{
if (request.RequestType == "Management")
{
Console.WriteLine($"Management: Handling request - {request.Description}");
}
else if (NextHandler != null)
{
NextHandler.HandleRequest(request);
}
}
}
Step 4. Set Up the Chain of Responsibility
We create the chain of handlers and pass a request through the chain.
class Program
{
static void Main(string[] args)
{
// Create handlers
var customerSupport = new CustomerSupportHandler();
var technicalSupport = new TechnicalSupportHandler();
var management = new ManagementHandler();
// Set up the chain
customerSupport.SetNextHandler(technicalSupport);
technicalSupport.SetNextHandler(management);
// Create requests
var request1 = new CustomerRequest("Customer Support", "Need help with my account.");
var request2 = new CustomerRequest("Technical Support", "Having trouble with the website.");
var request3 = new CustomerRequest("Management", "Feedback about service quality.");
// Pass requests through the chain
customerSupport.HandleRequest(request1);
customerSupport.HandleRequest(request2);
customerSupport.HandleRequest(request3);
}
}
Explanation
- Request Class: CustomerRequest represents a customer request with a type and description.
- Handler Interface: RequestHandler is an abstract class with a method to handle requests and a reference to the next handler.
- Concrete Handlers: CustomerSupportHandler, TechnicalSupportHandler, and ManagementHandler are concrete implementations that process specific types of requests.
- Setting Up the Chain: Handlers are linked together to form a chain. Requests are passed through the chain, starting from the first handler.
Output
When the program runs, it will produce the following output.
Customer Support: Handling request - Need help with my account.
Technical Support: Handling request - Having trouble with the website.
Management: Handling request - Feedback about service quality.
Conclusion
The Chain of Responsibility pattern is a powerful tool for handling requests in a flexible and decoupled manner. By using this pattern, you can create a system where multiple handlers can process a request, and the sender does not need to know which handler will ultimately handle it. This promotes separation of concerns and makes the system easier to maintain and extend. In our example, we demonstrated how to implement the Chain of Responsibility pattern in C# for a customer service request handling system.