Qt Basics: The Chain of Responsibility Pattern

By Pravin Kumar

1. Introduction

Chain of Responsibility is a concept in behavioral design patterns where the solution domain consists of solution modules in numbers and the client (problem) does not know actually which module in the solution domain would handle the problem. Complexity grows when a group of modules in the solution domain could together solve the problem and with every problem regrouping is required in the domain. Qt supports the chain of responsibility design through C++ classes and a stable framework can be developed. This article develops a stable framework that can be used in solving similar kinds of issues with already available design and C++ code.

2. Scenario

There are two scenarios presented here:

  1. A client needs to shuffle variable length strings.
  2. In a software installer, one requirement is that there is the possibility of different kinds of tasks required to solve different sets of problems from different client needs. Tasks can be the welcome window, install directory selection window, package selection window, license window, and so forth. All tasks should be arranged in a fashion that clients just need to know about the beginning. For any kind of requirement, the client should be able to call the beginning task for the solution, irrespective of the presence of many other tasks in the arrangement.

3. Issues

  1. Because string sizes would be different, the client cannot just write a nested for loop to shuffle the string. What the client would like to know is if there would be a generic entry point where he can call the start and then the string itself would take care of the shuffling.
  2. Because customers’ requirements can be different, he would need to just fetch the beginning task where he can just click next button to start the operation and all relevant tasks should appear as per the problem domain.

4. Solution

4.1 Design

  1. Shuffling a string can be solved through the chain of responsibility where a string should be considered as a chain of characters. A request to shuffle will be provided to the left-most character in the string. It is up to each character in the string to either handle the request or to pass it to the next character.
  2. An installer problem can be solved by arranging all the tasks in a chain. The first task in the chain would handle the request from the client domain. It is up to the task in the chain to either handle and serve the request or to pass it to the next task in the chain.

Chain01
Figure 1: A node in the chain

4.2 Class Diagram

4.2.1 Chain of Responsibility

A handler interface provides the basic methods; in other words, the request handler which is virtual and nodes in the chain that the subclass handler implements. The handler base class can provide basic functionality as to proceed to the next and previous situation. The handler base class also provides a virtual destructor where its destructor call will delete the next node in the chain.

  • Handler: Defines an interface for handling requests
  • Node: Handles the requests it is responsible for; if it cannot handle the request, it sends the request to its successor
  • Client: Sends commands to the first object in the chain that may handle the request

Chain02
Figure 2: The Chain of Responsibility

4.2.2 String Shuffling

  • Chain: Interface class keeps the request string to be shuffled
  • Digit: Node in a chain
  • printdigt: Prints the string

Chain03
Figure 3: String shuffling

4.2.3 Installer

  • The widget subclass, QWidget, creates the chain, draws the installer widget, and keeps requestlist read from request.txt.
  • Device is the chain interface and keeps the widget pointer.
  • frontdevice to lastdevice are various purpose dialog boxes or tasks.
  • request.txt carries the name of dialog boxes installer shows. For example, front, lic, dir, comp, menu, last. It can grow and as per the client need.
  • Selected dialog boxes can be put in request.txt for which the installer would show only those.

Chain04
Figure 4: The installer

5. How It Works

This pattern involves preparation of the chain where each node in a chain follows a similar interface. Each node carries a pointer to the next (and previous) nodes. Each problem scenario starts from the beginning node of the chain and each node in the chain behaves independently from others and handles the situation in its own fashion. Because each node follows the same interface and carries the addresses of next (and previous), it forwards the request to the neighbor node as per the need. This pattern creates a team effort to solve the problem, but there can be no mediator and each member does not know the others’ capability and just concentrate on finishing its own work if it can.

Chain05
Figure 5: The work pattern

  1. In string shuffling, each node is a character; it typically forwards the request after exchanging the character in a certain count where the count is provided to the node while forming the chain. It is actually the last node that knows how to print the data. Each node carrying its responsibility actually makes the work happen.
  2. In the installer scenario, each node in the chain first checks if it is supposed to handle the request and then it displays its user interface and lets the user interact with it before it forwards the request to its neighbor.

6. Program

6.1 Shuffling String

Editor’s Comment: In this and the following listings, comments marked with “//” and text, all in red, are author-provided annotations to the code.

<main.cpp>
QString toshufflestring=argv[1];
chain *ch=NULL,*tch;
for (i=0;i<toshufflestring.size();i++){
   if(ch==NULL)
      // Prepares the chain
      ch=tch=new digit(toshufflestring.size()-i-1,NULL);
   else{
      tch->next=new digit(toshufflestring.size()-i-1,NULL);
      tch=tch->next;
   }
}
if(ch!=NULL){
   // Prepares the request
   *ch->str=toshufflestring;
   // Calls first node in the chain to handle the request
   ch->process();
}
// Deletes the first node in the chain
delete ch;

<<chain.cpp>
chain::~chain(){
   if(str!=NULL) {
      delete str;
      str=NULL;
   }
   // Deletes the next node in the chain
   if(next!=NULL) delete next;
}

6.2 Installer

<widget.cpp>>
widget::widget(QWidget *p):QWidget(p){
   ...
   ...
   // Prepares the request
   rlst=new QStringList;

*(QStringList*)rlst=QString(file.readAll()).
   split(QRegularExpression("\\s+"),
   QString::SkipEmptyParts);
file.close();
tdv=dv=new frontdevice(NULL,new
   licensedevice(NULL,new dirdevice(NULL,new
   treedevice(NULL, new main
   // Preparing the chain
   menudevice(NULL,
   new lastdevice(NULL,NULL))))));
while(tdv->next!=NULL){
   tdv->next->prev=tdv;
   tdv=tdv->next;
}
...
setLayout(hlt);
// Placing request to first node
dv->work();
...
}

widget::~widget(){
   ...
   // Just delete the first node in the chain
   ....
}

void device::work(){
   int index=0;
   while(index!=((QStringList*)wdgt->rlst)->size()){
      if(((QStringList*)wdgt->rlst)->at(index)==*
         (QString*)identity()) break;
      ++index;
   }
   // Handling the request
   if(index<((QStringList*)wdgt->rlst)->size()){
      wdgt->layout()->removeItem(wdgt->vlt);
      ((QHBoxLayout*)wdgt->layout())->
         insertLayout(1,vlt,1);
      wdgt->vlt=vlt;
      unhide();
   // Request passed
   }else
   if(pbnext && next != 0){
      hide();
      next->work();
   }else
   if(!pbnext && prev !=0){
      hide();
      prev->work();
   }
}

7. Experimental Data

7.1 Shuffler

>shuffle.exe “1212”

1212
1221
1122
2121
2112
2211

>shuffle.exe “1abc”

1abc
1acb
...
...
c1ab
c1ba
...
...
cb1a
cba1

7.2 Installer

Request to show all tasks:

> cat Request.txt

front lic dir comp menu last

Chain06
Figure 6: Welcome window

Chain07
Figure 7: License agreement window

Chain08
Figure 8: Directory selection window

Chain09
Figure 9: Component selection window

Chain10
Figure 10: Main menu entry window

Chain11
Figure 11: Last or install window

> cat Request.txt

front dir last

Chain12
Figure 12: Last window

8. Summary

This article, in addition to providing information about the chain of responsibility concepts in behavioral design patterns, also shows the way to implement this pattern in Qt. It includes examples on shuffling a string and writing a software installer, both based upon Qt, where both are achieved through preparing a chain of nodes where each node follows the same interface and shifting on nodes in a chain happen through internal decisions inside the chain. the client just needs to fire the beginning of the chain irrespective of a different problem domain.

9. Reference

Qt 5.2 documentation

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read