9.10. Cadena de Responsabilidad: intentar una secuencia de estrategias

Debe pensarse en Cadena de Responsabilidad como en una generalización dinámica de la recursión, usando objetos Estrategia. Se hace una llamada y cada Estrategia de la secuencia intenta satisfacer la llamada. El proceso termina cuando una de las Estrategias tiene éxito o la cadena termina. En la recursión, una función se llama a sí misma una y otra vez hasta que se alcanza una condición de finalización; con Cadena de Responsabilidad, una función se llama a sí misma, la cual (moviendo la cadena de Estrategias) llama a una implementación diferente de la función, etc, hasta que se alcanza la condición de finalización. Dicha condición puede ser que se ha llegado al final de la cadena (lo que devuelve un objeto por defecto; puede que no sea capaz de proporcionar un resultado por defecto, así que debe ser capaz de determinar el éxito o fracaso de la cadena) o que una de las Estrategias ha tenido éxito.

En lugar de llamar a una única función para satisfacer una petición, hay múltiples funciones en la cadetna que tienen la oportunidad de hacerlo, de manera que tiene el aspecto de un sistema experto. Dado que la cadena es en la práctica una lista, puede crearse dinámicamente, así que podría verse como una sentencia switch más general y construida dinámicamente.

En el GoF, hay bastante discusión sobre cómo crear la cadena de responsabilidad como una lista enlazada. Sin embargo, cuando se estudia el patrón, no debería importar cómo se crea la cadena; eso es un detalle de implementación. Como el GoF se escribió antes de que los contenedores STL estuvieran disponibles en la mayoría de los compiladores de C++, las razones más probables son (1) que no había listas incluídas y por lo tanto tenían que crear una y (2) que las estructuras de datos suelen verse como una habilidad fundamental en las Escuelas (o Facultades), y a los autores del GoF no se les ocurrió la idea de que las estructuras de datos fueran herramientas estándar disponibles junto con el lenguaje de programación.. Los detalles del contenedor usado para implementar la Cadena de Responsabilidad como una cadena (una lista enlazada en el GoF) no añaden nada a la solución, y puede implementarse usando un contenedor STL, como se muestra abajo.

Aquí puede ver una Cadena de Responsabilidad que encuentra automáticamente una solución usando un mecanismo para recorrer automática y recursivamente cada Estrategia de la cadena:

//: 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();
} ///:~

Listado 9.22. C10/ChainOfReponsibility.cpp


Observe que la clase de Contexto Gimme y todas las clases Estrategia derivan de la misma clase base, GimmeStrategy.

Si estudia la sección sobre Cadena de Responsabilidad del GoF, verá que la estructura difiere significativamente de la que se muestra más arriba, porque ellos se centran en crear su propia lista enlazada. Sin embargo, si mantiene en mente que la esencia de Cadena de Responsabilidad es probar muchas soluciones hasta que encuentre la que funciona, se dará cuenta de que la implementación del mecanismo de secuenciación no es parte esencial del patrón.