Introduction
Hi all. This is Acharya, a passionate object oriented designer and programmer. From couple of days I started learning design patterns and trying to create code samples with real world examples to remember them easily.
In this article I would like to share my knowledge and an easy example for Chain of Responsibility design pattern. Let’s get in...
Understanding the Pattern
Chain of Responsibility is one of the powerful behavioral design pattern in GOF patterns that de-couples the sender of a request to its receiver by giving more than one object a chance to handle the request. It holds a chain of receiving objects and pass the request along the chain until an object handles it.
When I read the above statement/definition for the first time I was little bit confusing and I was not able to relate it to real time implementation. I guess you too facing the same. So let’s break the statement into small chunks and co-relate each one with real world example.
let’s assume a small client who want to conduct some quiz competitions and asked us to write an application program for that.
They want the software should be generic enough so that they can input any number of questions, participants and their seating arrangements for each event dynamically.
Here is the list of requirements given by the client.
- All participants will be seated side by side.
- No.of participants vary from event to event.
- Order of participants will vary from event to event.
- We are not sure who will be able to answer which question.
- Quiz event host will start asking the first question to first person in order.
- If he answers, then a point will be added to his total score and host will proceed with next question to next person.
- If any participant failed to answer the question then the host will pass that question to the next person in the queue/line.
- This process continues till we get the answer or all participants fail to answer that question.
- The quiz will be continued till all questions are completed irrespective of how many got answered by participants.
- At the end, results should be announced for all participants.
Now let’s co-relate the Chain of Responsibility Design Pattern definition with above example.
- Here receiving objects are the participants.
- Sender of a request is the quiz event host.
- Request is the question to be answered by participants.
- Handling request is nothing but giving correct answer for the question.
As we don’t know who has sufficient knowledge to answer the question the event host will pass on the question to a sequence of participants where the sequence is dynamic. Each question will be passed in a sequence till we get the correct answer or all participants should fail to give correct answer. If all participants failed to answer a question, then the host will pick up the next question and proceed with the process.
Implementation
Now let’s start with implementation.
Here we have three key objects.
- Question
- Participant
- Event Host
Let’s implement Question and Answer logic first. Here the Answer is marked as private field to provide security. Its visibility is restricted to Question class and the organizer/host (will discuss about organizer/host at later point in this article). We have a pointer to point the original participant to whom this question was initially asked. CheckAnswer() method validates the answer given by participants.
interface IQuestion
{
string Question1 { get; set; }
IParticipant Owner { get; set; }
bool CheckAnswer(string ans);
}
class Question : IQuestion
{
public string Question1 { get; set; }
public IParticipant Owner { get; set; }
private string Answer;
public Question(string q, string a)
{
Question1 = q;
Answer = a;
}
public bool CheckAnswer(string ans)
{
return ans.Equals(Answer, StringComparison.InvariantCultureIgnoreCase);
}
}
Now coming to Participants, Here every one has Name, Score property and a pointer to point their next participant.
interface IParticipant
{
string Name { get; set; }
int Score { get; set; }
IParticipant NextParticipent { get; set; }
void DoAnswer(IQuestion question);
}
class Participant : IParticipant
{
public IParticipant NextParticipent { get; set; }
public string Name { get; set; }
public int Score { get; set; }
public Participant(IParticipant nextParticipent)
{
NextParticipent = nextParticipent;
Score = 0;
}
public void DoAnswer(IQuestion question)
{
Console.WriteLine("Hi " + Name + ", Please answer for the question " + question.Question1);
string ans = Console.ReadLine();
if (question.CheckAnswer(ans))
{
Console.WriteLine("Correct Answer");
this.Score += 1;
}
else if (NextParticipent != question.Owner)
{
Console.WriteLine("Wrong Answer. Pass on to next participent.");
NextParticipent.DoAnswer(question);
}
else
{
Console.WriteLine("No one answered");
}
}
}
If you look at the DoAnswer() method we will understand the core logic which is described in Chain of Responsibility design pattern. If the participants given answer is correct then increment his score with 1 and return. Else pass on the question to next participant till it is answered or the next pointed participant is the initial owner of this question.
Now it’s time to implement Event host. Here the duty of event host is to coordinate and conduct quiz with given list of questions and participants by client.
class QuizHost
{
private List<IQuestion> _questions;
private List<IParticipant> _participents;
public QuizHost(List<IQuestion> questions, List<IParticipant> participents)
{
_questions = questions;
_participents = participents;
}
public void StartEvent()
{
Console.WriteLine("Start of quiz. Welcome All.\n");
IParticipant currentParticipent = _participents.First();
foreach (var q in _questions)
{
q.Owner = currentParticipent;
currentParticipent.DoAnswer(q);
currentParticipent = currentParticipent.NextParticipent;
}
Console.WriteLine("\nEnd of quiz. Score card.");
foreach (var p in _participents)
{
Console.WriteLine(p.Name + " : " + p.Score);
}
Console.WriteLine("\nThanks for participating.");
}
}
Using the code
Now the main organizer who is our client will come and feed the dynamic inputs.
class Program
{
static void Main(string[] args)
{
#region List of Participents
IParticipant p3 = new Participant(null) { Name = "Raja" };
IParticipant p2 = new Participant(p3) { Name = "Sree" };
IParticipant p1 = new Participant(p2) { Name = "Sumesh" };
p3.NextParticipent = p1;
List<IParticipant> participents = new List<IParticipant>() { p1, p2, p3 };
#endregion
#region List of Questions
IQuestion q1 = new Question("Is CPU a part of computer?", "Yes");
IQuestion q2 = new Question("Is IAS a computer course?", "No");
IQuestion q3 = new Question("Is computer an electronic device?", "Yes");
List<IQuestion> questions = new List<IQuestion>() { q1, q2, q3 };
#endregion
QuizHost myQuiz = new QuizHost(questions, participents);
myQuiz.StartEvent();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
Just run this program and see the magic. Surely you will enjoy the beauty of this design pattern.
Complete source code for your reference
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
#region List of Participents
IParticipant p3 = new Participant(null) { Name = "Raja" };
IParticipant p2 = new Participant(p3) { Name = "Sree" };
IParticipant p1 = new Participant(p2) { Name = "Sumesh" };
p3.NextParticipent = p1;
List<IParticipant> participents = new List<IParticipant>() { p1, p2, p3 };
#endregion
#region List of Questions
IQuestion q1 = new Question("Is CPU a part of computer?", "Yes");
IQuestion q2 = new Question("Is IAS a computer course?", "No");
IQuestion q3 = new Question("Is computer an electronic device?", "Yes");
List<IQuestion> questions = new List<IQuestion>() { q1, q2, q3 };
#endregion
QuizHost myQuiz = new QuizHost(questions, participents);
myQuiz.StartEvent();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
class QuizHost
{
private List<IQuestion> _questions;
private List<IParticipant> _participents;
public QuizHost(List<IQuestion> questions, List<IParticipant> participents)
{
_questions = questions;
_participents = participents;
}
public void StartEvent()
{
Console.WriteLine("Start of quiz. Welcome All.\n");
IParticipant currentParticipent = _participents.First();
foreach (var q in _questions)
{
q.Owner = currentParticipent;
currentParticipent.DoAnswer(q);
currentParticipent = currentParticipent.NextParticipent;
}
Console.WriteLine("\nEnd of quiz. Score card.");
foreach (var p in _participents)
{
Console.WriteLine(p.Name + " : " + p.Score);
}
Console.WriteLine("\nThanks for participating.");
}
}
interface IQuestion
{
string Question1 { get; set; }
IParticipant Owner { get; set; }
bool CheckAnswer(string ans);
}
class Question : IQuestion
{
public string Question1 { get; set; }
public IParticipant Owner { get; set; }
private string Answer;
public Question(string q, string a)
{
Question1 = q;
Answer = a;
}
public bool CheckAnswer(string ans)
{
return ans.Equals(Answer, StringComparison.InvariantCultureIgnoreCase);
}
}
interface IParticipant
{
string Name { get; set; }
int Score { get; set; }
IParticipant NextParticipent { get; set; }
void DoAnswer(IQuestion question);
}
class Participant : IParticipant
{
public IParticipant NextParticipent { get; set; }
public string Name { get; set; }
public int Score { get; set; }
public Participant(IParticipant nextParticipent)
{
NextParticipent = nextParticipent;
Score = 0;
}
public void DoAnswer(IQuestion question)
{
Console.WriteLine("Hi " + Name + ", Please answer for the question " + question.Question1);
string ans = Console.ReadLine();
if (question.CheckAnswer(ans))
{
Console.WriteLine("Correct Answer");
this.Score += 1;
}
else if (NextParticipent != question.Owner)
{
Console.WriteLine("Wrong Answer. Pass on to next participent.");
NextParticipent.DoAnswer(question);
}
else
{
Console.WriteLine("No one answered");
}
}
}
History
- Initial version - 23rd Dec 2015