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:
- A client needs to shuffle variable length strings.
- 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
- 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.
- 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
- 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.
- 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.
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
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
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.
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.
Figure 5: The work pattern
- 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.
- 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
Figure 6: Welcome window
Figure 7: License agreement window
Figure 8: Directory selection window
Figure 9: Component selection window
Figure 10: Main menu entry window
Figure 11: Last or install window
> cat Request.txt
front dir last
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