9.6. Desacoplamiento de objetos

Tanto el Proxy como el Estado proporcionen una clase sucedánea. El código habla a esta clase sucedánea, y la verdadera clase que hace el trabajo está escondida detrás de la sucedánea. Cuando usted llama a una función en la clase sucedánea, simplemente da un rodeo y llama a la función en la clase implementadora. Estos dos patrones son tan familiares que, estructuralmente, Proxy es un caso especial de Estado. Uno está tentado de juntar ambos en un patrón llamado Sucedánea, pero la intención de los dos patrones es distinta. Puede ser fácil caer en la trampa de pensar que si la estructura es la misma, los patrones son el mismo. Debe mirar siempre la intención del patrón para tener claro lo que hace.

La idea básica es simple: cree una clase base, la sucedánea se deriva junto con la clase o clases que aportan la siguiente implementación:

Cuando se crea una clase sucedánea, se le da una implementación a la que envía las llamadas a función.

Estructuralmente, la diferencia entre Proxy y Estado es simple: un Proxy sólo tiene una implementación, mientras que Estado tiene más de una. La aplicación de los patrones se considera (en el GoF) distinta: Proxy controla el acceso a su implementación, mientras que Estado cambia la implementación dinámicamente. Sin embargo, si se amplía la noción de "controlar el acceso a la implementación", entonces los dos parecen ser parte de un todo.

9.6.1. Proxy: FIXME: hablando en nombre de otro objeto

Si se implementa un Proxy usando el diagrama anterior, tiene esta pinta:

//: C10:ProxyDemo.cpp
// Simple demonstration of the Proxy pattern.
#include <iostream>
using namespace std;

class ProxyBase {
public:
  virtual void f() = 0;
  virtual void g() = 0;
  virtual void h() = 0;
  virtual ~ProxyBase() {}
};

class Implementation : public ProxyBase {
public:
  void f() { cout << "Implementation.f()" << endl; }
  void g() { cout << "Implementation.g()" << endl; }
  void h() { cout << "Implementation.h()" << endl; }
};

class Proxy : public ProxyBase {
  ProxyBase* implementation;
public:
  Proxy() { implementation = new Implementation(); }
  ~Proxy() { delete implementation; }
  // Forward calls to the implementation:
  void f() { implementation->f(); }
  void g() { implementation->g(); }
  void h() { implementation->h(); }
};

int main()  {
  Proxy p;
  p.f();
  p.g();
  p.h();
} ///:~

Listado 9.14. C10/ProxyDemo.cpp


En algunos casos, Implementation no necesita la misma interfaz que Proxy, siempre y cuando Proxy esté de alguna forma hablando en nombre de la clase Implementación y referenciando llamadas a función hacia ella, entonces la idea básica se satisface (note que esta afirmación está reñida con la definición de Proxy del GoF). Sin embargo, con una interfaz común es posible realizar un reemplazo FIXME: drop-in del proxy en el código del cliente -el código del cliente está escrito para hablar al objeto original, y no necesita ser cambiado para aceptar el proxy (éste es probablemente el quiz principal de Proxy). Además, se fuerza a que Implementation complete, a través de la interfaz común, todas las funciones que Proxy necesita llamar.

La diferencia entre Proxy y Estado está en los problemas que pueden resolver. Los usos más comunes para Proxy que describe el GoF son:

1. Proxy remoto. Representan a objetos en un espacio de direcciones distinto. Lo implementan algunas tecnologías de objetos remotos.

2. Proxy virtual. Proporciona inicialización FIXME: vaga para crear objetos costosos bajo demanda.

3. Proxy de protección. Se usa cuando no se desea que el programador cliente tenga acceso completo al objecto representado.

4. Referencia inteligente. Para añadir acciones adicionales cuando se acceda al objeto representado. El conteo de referencias es un buen ejemplo: mantiene un registro del número de referencias que se mantienen para un objeto en particular, para implementar el FIXME: copy-on-write idiom y para prevenir el FIXME: object aliasing.