Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
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 |