Interactive Q&A With Chain of Responsibility and How to Use It
Chain of responsibility is a behavioural design pattern that allows a response to be passed along a chain of handlers. Each handler receives the request in sequence and optionally acts on it before passing it on to the next handler. This design pattern is useful to cases where processing of a request is done in a sequence of steps. By using handler for each of the step, the code can be loosely coupled with the main program and easy to maintain in the future.
One example of using this design pattern is dealing with an interactive Q&A where question is chosen dynamically based on the previous answer.
The imaginary problem
We need to build an interactive Q&A module to be presented to users. When users select an answer, the next question is then displayed based on the answer of previous question, and so on. In other words, depending on the request, different question will be picked by the system.
Solution
Here we can imagine the answer from users is a request that is being sent to the system, and the system has to decide which question in the question bank to be displayed next. The system can make the decision by forming a chain of question handlers, where each handler represents one question and makes decision for its corresponding question. If the decision made is not to display the question, the handler passes the request on to the next handler, until the decision to display is made.
The question handler, or handler in general, has to implement at least two methods: SetNextHandler(IHandler handler)
and Handle()
:
public interface IHandler
{
Handler SetNextHandler(IHandler handler);
object Handle();
}
Using interface is optional if there is only one type of handler we need. In our case, we only have question handler. Our question handler can be written like the following:
public class QuestionHandler
{
private readonly Question _question;
private QuestionHandler _nextQuestionHandler; public QuestionHandler(Question question)
{
_question = question;
} public SetNextHandler(QuestionHandler handler)
{
_nextQuestionHandler = handler;
} public Question Handle(string request)
{
//check condition to load, if the condition is true, then return the question, if not, then pass on to the next handler.
if(isConditionFulfilled(condition)
return question;
return _nextQuestionHandler.Handle(request)
}
}
With the question handler ready, we can then create the instances of question handler in the client application:
//create handler for each question and link the handlers in sequence/chain
var questionHandlers = new List<QuestionHandler>();
foreach(var question in GetAllQuestions())
questionHandlers.Add(new QuestionHandler(question));
for(int i = 0; i < questionHandlers.Count - 1; i++)
questionHandlers[I].SetNextHandler(questionHandlers[i+1]);//when request comes in, start the chain from the first (usually) handler
var questionToDisplay = questionHandlers[0].Handle(request);
The questionToDisplay
will be the question that is returned by the chain formed by the list of question handlers.
Conclusion and what’s next
This is a fast way to design a Q&A system. However, the performance or speed of system to search for the correct question to display can be the bottleneck. Imagine the question is at the end of a very long chain, the waiting time will be much longer. This can be overcome by improving the data structure and algorithm.