martes, 31 de agosto de 2010

Patrones de diseño: Proxy

Según la Wikipedia, el patrón Proxy se utiliza como intermediario para acceder a un objeto, permitiendo controlar el acceso a él.

Este patrón es ampliamente utilizado en frameworks cómo Hibernate o Spring AOP, permitiendo capturar las llamadas a objetos POJO y permitiendo insertar en ellas capacidades de persistencia para el caso de Hibernate, u otro tipo de aspectos como gestión de seguridad o transacciones para Spring AOP.

A continuación mostramos el diagrama de clases del patrón Proxy:

En este patrón se identifica una interfaz Subject, un objeto RealSubject que es el objeto al que se accede a través del Proxy. El objeto Proxy mantiene una referencia al objeto RealSubject y controla el acceso a sus métodos, intruciendo las capacidades adicionales que fuesen necesarias. Tanto la clase RealSubject como la clase Proxy implementan la interfaz Subject.

En Java es fácil crear proxies dinámicos en tiempo de ejecución a partir de la clase java.lang.reflect.Proxy. Veámoslo a través de un ejemplo.

Creamos una interfaz:
package com.roldan.proxy;

public interface Lector {
public void leer();
}

Ahora creamos una implementación para esta interfaz:
package com.roldan.proxy;

public class LectorImpl implements Lector {

public void leer(){
System.out.println("Estamos leyendo...");
}
}

En este momento, creamos el proxy para la clase LectorImpl:
package com.roldan.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class LectorProxy
implements java.lang.reflect.InvocationHandler {

private Object obj;

public static Object newInstance(Object obj) {
return java.lang.reflect.Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new LectorProxy(obj));
}

private LectorProxy(Object obj) {
this.obj = obj;
}

public Object invoke(
Object proxy,
Method m,
Object[] args) throws Throwable {
Object result;
try {
System.out.println(
"Antes de llamar al metodo " + m.getName());
result = m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
System.out.println(
"Despues de llamar al metodo " + m.getName());
}
return result;
}
}

Esta clase, que usa el patrón singleton, crea dinámicamente un proxy para la instancia de la clase que se pasa a su método newInstance() gracias a las capacidades de la clase java.lang.reflect.Proxy.

El método invoke() de esta clase captura las invocaciones a este proxy y las redirige al objeto LectorImpl en lugar del que actúa, añadiéndolo funcionalidad adicional, que en este caso consiste en mostrar mensajes antes y después del método invocado.

Ahora podemos crear una instancia de este proxy y acceder a los métodos de la interfaz Lector que implementa:
package com.roldan.proxy;

public class PruebaLector {
public static void main(String[] args) {
Lector lector = (Lector)LectorProxy.newInstance(
new LectorImpl());
lector.leer();
}
}

Si ejecutamos la prueba, obtenemos la siguiente salida por la consola:
Antes de llamar al metodo leer
Estamos leyendo...
Despues de llamar al metodo leer


Vemos que la llamada al método leer() gestionada por el proxy ha sido adornada con los dos mensajes adicionales.

A pesar de la sencillez de este ejemplo, el uso de proxies dinámicos permite la ejecución de tareas avanzadas en frameworks de gran complejidad como Hibernate o Spring AOP.

Referencias:
Patrón de diseño Proxy en Wikipedia
¿Cuál es el problema que resuelve el patrón de diseño Proxy?
Patrón de diseño Proxy en Adictos al Trabajo
Dynamic Proxy Classes
Otros patrones de diseño

2 comentarios:

  1. Hola, me parece que la relacion entre Agente e Implementacion con "Interface" es de "Realizacion". Es igual a la herencia, pero con lineas discontinuas, es decir, de dependencia con herencia. Saludos

    ResponderEliminar
  2. Tienes razón, ya he cambiado el diagrama para corregirlo.

    Un saludo.

    ResponderEliminar