User Tools

Site Tools


wiki:pdd_4

Proxy

Contexto: Suponga una clase BookSearch, cuyo principal método busca por un libro, dado el ISBN:

class BookSearch {
  ...
  Book getBook(String ISBN) { ... }
  ...
}

Problema: Suponga que nuestro servicio de búsqueda de libros se esté volviendo popular y ganando usuarios. Para mejorar su rendimiento, pensamos en introducir un sistema de caché: antes de buscar un libro, verificaremos si está en el caché. Si es así, el libro se devolverá inmediatamente. De lo contrario, la búsqueda continuará según la lógica normal del método getBook(). Sin embargo, no nos gustaría que este nuevo requisito — la búsqueda en caché — se implementara en la clase BookSearch. La razón es que queremos mantener la clase cohesiva y adherente al Principio de Responsabilidad Única. De hecho, el caché será implementado por un desarrollador diferente del que es responsable de mantener BookSearch. Además, vamos a utilizar una biblioteca de caché de terceros, con diversas funcionalidades y personalizaciones. Por ello, consideramos importante separar, en clases distintas, el interés de buscar libros por ISBN (que es un requisito funcional) del interés de utilizar un caché en las búsquedas de libros (que es un requisito no funcional).

Solución: El patrón de diseño Proxy propone la inserción de un objeto intermediario, llamado proxy, entre un objeto base y sus clientes. Así, los clientes ya no tendrán una referencia directa al objeto base, sino al proxy. A su vez, el proxy tiene una referencia al objeto base. Además, el proxy debe implementar las mismas interfaces que el objeto base.

El objetivo de un proxy es mediar el acceso a un objeto base, agregándole funcionalidades, sin que este tenga conocimiento de ello. En nuestro caso, el objeto base es de tipo BookSearch; la funcionalidad que queremos agregar es un caché; y el proxy es un objeto de la siguiente clase:

class BookSearchProxy implements BookSearchInterface {
 
  private BookSearchInterface base;
 
  BookSearchProxy (BookSearchInterface base) {
    this.base = base;
  }
 
  Book getBook(String ISBN) {
    if("libro con ISBN en el cache")
      return "libro del cache"
    else {
      Book book = base.getBook(ISBN);
      if(book != null)
        "adicione book en el cache"
      return book;
    }
  }
  ...
}

También debe crearse una interfaz BookSearchInterface, que no se muestra en el código. Tanto la clase base como la clase proxy deben implementar esta interfaz. Esto permitirá que los clientes no sean conscientes de la existencia de un proxy entre ellos y el objeto base. Una vez más, estamos aplicando el principio de Preferir Interfaces a Clases.

El siguiente código ilustra la instanciación del proxy. Primero, mostramos el código antes del proxy. En ese código (a continuación), un objeto BookSearch es creado en el programa principal y luego pasado como parámetro a cualquier clase o función que necesite el servicio de búsqueda de libros, como la clase View.

void main() {
  BookSearch bs = new BookSearch();
  ...
  View view = new View(bs);
  ...
}

Con la decisión de usar un proxy, tendremos que modificar este código para instanciar el proxy (código a continuación). Además, View ahora recibe como parámetro en su constructor una referencia al proxy, en lugar de una referencia al objeto base.

  BookSearch bs = new BookSearch();
  BookSearchProxy pbs;
  pbs = new BookSearchProxy(bs);
  ...
  View view = new View(pbs);
  ...

La próxima figura ilustra los objetos y las referencias entre estos objetos, considerando la solución de usar un proxy.

Además de ayudar en la implementación de cachés, los proxies pueden usarse para implementar otros requisitos no funcionales. Algunos ejemplos incluyen:

  • Comunicación con un cliente remoto, es decir, se puede usar un proxy para encapsular protocolos y detalles de comunicación. Estos proxies son llamados stubs.
  • Asignación de memoria bajo demanda para objetos que consumen mucha memoria. Por ejemplo, una clase puede manejar una imagen de alta resolución. Entonces, podemos usar un proxy para evitar que la imagen esté cargada en la memoria principal todo el tiempo. Solo se cargará, posiblemente desde el disco, antes de la ejecución de algunos métodos.
  • Controlar el acceso de varios clientes a un objeto base. Por ejemplo, los clientes deben estar autenticados y tener permiso para ejecutar ciertas operaciones del objeto base. Con esto, la clase del objeto base puede centrarse en la implementación de los requisitos funcionales.
wiki/pdd_4.txt · Last modified: 2024/09/06 09:50 by admin