9.6.2. Estado: cambiar el comportamiento del objeto

El patrón Estado produce un objeto que parece que cambia su clase, y le será útil cuando descubra que tiene código condicional en todas o casi todas sus funciones. Al igual que Proxy, un Estado se crea teniendo un objeto front-end que usa un objeto back-end de implementación para completar sus tareas. Sin embargo, el patrón Estado alterna entre una implementación y otra durante la vida del objeto front-end, para mostrar un comportamiento distinto ante las mismas llamadas a función. Es una forma de mejorar la implementación de su código cuando realiza un montón de pruebas en cada una de sus funciones antes de decidir qué hacer con esa función. Por ejemplo, el cuento del príncipe convertido en rana contiene un objeto (la criatura) que se comporta de modo distinto dependiendo del estado en el que se encuentre. Podría implementar esto comprobando un bool:

//: C10:KissingPrincess.cpp
#include <iostream>
using namespace std;

class Creature {
  bool isFrog;
public:
  Creature() : isFrog(true) {}
  void greet() {
    if(isFrog)
      cout << "Ribbet!" << endl;
    else
      cout << "Darling!" << endl;
  }
  void kiss() { isFrog = false; }
};

int main() {
  Creature creature;
  creature.greet();
  creature.kiss();
  creature.greet();
} ///:~

Listado 9.15. C10/KissingPrincess.cpp


Sin embargo, la función greet(), y cualquier otra función que tenga que comprobar isFrog antes de realizar sus operaciones, acaban con código poco elegante, especialmente cuando haya que añadir estados adicionales al sistema. Delegando las operaciones a un objeto Estado que puede cambiarse, el código se simplifica.

//: C10:KissingPrincess2.cpp
// The State pattern.
#include <iostream>
#include <string>
using namespace std;

class Creature {
  class State {
  public:
    virtual string response() = 0;
  };
  class Frog : public State {
  public:
    string response() { return "Ribbet!"; }
  };
  class Prince : public State {
  public:
    string response() { return "Darling!"; }
  };
  State* state;
public:
  Creature() : state(new Frog()) {}
  void greet() {
    cout << state->response() << endl;
  }
  void kiss() {
    delete state;
    state = new Prince();
  }
};

int main() {
  Creature creature;
  creature.greet();
  creature.kiss();
  creature.greet();
} ///:~

Listado 9.16. C10/KissingPrincess2.cpp


No es necesario hacer las clases FIXME: implementadoras anidadas ni privadas, pero si lo hace, el código será más limpio.

Note que los cambios en las clases Estado se propagan automáticamente por todo su código, en lugar de requerir una edición de las clases para efectuar los cambios.