Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

Chain of Responsibility: trying a sequence of strategies

Chain of Responsibility might be thought of as a dynamic generalization of recursion using Strategy objects. You make a call, and each Strategy in a linked sequence tries to satisfy the call. The process ends when one of the strategies is successful or the chain ends. In recursion, one function calls itself over and over until a termination condition is reached; with Chain of Responsibility, a function calls itself, which (by moving down the chain of Strategies) calls a different implementation of the function, etc., until a termination condition is reached. The termination condition is either that the bottom of the chain is reached (this returns a default object; you may or may not be able to provide a default result so you must be able to determine the success or failure of the chain) or one of the Strategies is successful.

Instead of calling a single function to satisfy a request, multiple functions in the chain have a chance to satisfy the request, so it has the flavor of an expert system. Since the chain is effectively a list, it can be dynamically created, so you could also think of it as a more general, dynamically-built switch statement.

In GoF, there s a fair amount of discussion of how to create the chain of responsibility as a linked list. However, when you look at the pattern it really shouldn t matter how the chain is created; that s an implementation detail. Since GoF was written before the STL containers were available in most C++ compilers, the reason for this is most likely (1) there was no built-in list and thus they had to create one and (2) data structures are often taught as a fundamental skill in academia, and the idea that data structures should be standard tools available with the programming language may not have occurred to the GoF authors. We maintain that the details of the container used to implement Chain of Responsibility as a chain (in GoF, a linked list) adds nothing to the solution and can just as easily be implemented using an STL container, as shown below.

Here you can see Chain of Responsibility automatically finding a solution using a mechanism to automatically recurse through each Strategy in the chain:

//: C10:ChainOfReponsibility.cpp
// The approach of the five-year-old.
#include <iostream>
#include <vector>
#include "../purge.h"
using namespace std;
 
enum Answer { NO, YES };
 
class GimmeStrategy {
public:
virtual Answer canIHave() = 0;
virtual ~GimmeStrategy() {}
};
 
class AskMom : public GimmeStrategy {
public:
Answer canIHave() {
cout << "Mooom? Can I have this?" << endl;
return NO;
}
};
 
class AskDad : public GimmeStrategy {
public:
Answer canIHave() {
cout << "Dad, I really need this!" << endl;
return NO;
}
};
 
class AskGrandpa : public GimmeStrategy {
public:
Answer canIHave() {
cout << "Grandpa, is it my birthday yet?" << endl;
return NO;
}
};
 
class AskGrandma : public GimmeStrategy {
public:
Answer canIHave() {
cout << "Grandma, I really love you!" << endl;
return YES;
}
};
 
class Gimme : public GimmeStrategy {
vector<GimmeStrategy*> chain;
public:
Gimme() {
chain.push_back(new AskMom());
chain.push_back(new AskDad());
chain.push_back(new AskGrandpa());
chain.push_back(new AskGrandma());
}
Answer canIHave() {
vector<GimmeStrategy*>::iterator it = chain.begin();
while(it != chain.end())
if((*it++)->canIHave() == YES)
return YES;
// Reached end without success...
cout << "Whiiiiinnne!" << endl;
return NO;
}
~Gimme() { purge(chain); }
};
 
int main() {
Gimme chain;
chain.canIHave();
} ///:~
 

Notice that the Context class Gimme and all the Strategy classes are all derived from the same base class, GimmeStrategy.

If you study the section on Chain of Responsibility in GoF, you ll find that the structure differs significantly from the one above because they focus on creating their own linked list. However, if you keep in mind that the essence of Chain of Responsibility is to try a number of solutions until you find one that works, you ll realize that the implementation of the sequencing mechanism is not an essential part of the pattern.

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire