Patrón Proxy: explicación, ejemplo y casos de uso

En este blog, normalmente hablamos de patrones asociados a la implementación de arquitecturas basadas en microservicios, sin embargo, contar con microservicios que sean sólidos en su arquitectura interna requiere de aplicar patrones «más clásicos» que llevan años en la industria del software; en una entrada hace unos meses hablamos de Strategy de la banda de los GoF, en esta entrada abordaremos el Patrón Proxy.

¿Qué es un proxy?

Quisiera comenzar explicando algunos elementos teóricos, indicando primeramente que es servidor proxy, algo con lo que casi todos los que trabajamos con sistemas informáticos estamos familiarizados.

«Un servidor proxy es un ordenador que puede conectarse como interfaz entre dos ordenadores o redes. Asume una función de intermediario, recibiendo peticiones y transmitiéndoles con su propia dirección IP a otra red. Los servidores proxy se pueden utilizar en Internet para ocultar la dirección IP.»

Basados en este concepto, el patrón proxy en el desarrollo de software tiene como objetivo desarrollar la misma función de ser un intermediario que agrega funcionalidad a una clase sin tocar la misma.

¿Qué es en realidad el Patrón Proxy?

La definición del patrón proxy en sí se pone en práctica a través del concepto de «Proxy Object», o en español un Objeto Proxy.

Un objeto proxy podemos definirlo como un objeto que agrega lógica adicional por encima del objeto que está siendo «proxiado» sin necesidad de modificar el código del objeto «proxiado». Un objeto proxy tiene los mismos métodos públicos que el objeto «proxiado» y debería ser lo más posible indistinguible. Cuando un método es invocado en el objeto proxy, el código adicional es usualmente ejecutado antes y/o después de ser ejecutado el método en el objeto «proxiado».

La siguiente figura ilustra el concepto anterior:

Objeto proxy (elaboración propia)

Veamos ahora, a través de un ejemplo aplicado con Java este concepto, tengamos en cuenta como característica principal que el objeto que hace de proxy tiene como mínimo las mismas funcionalidad que el objeto proxiado.

Ejemplo en Java del Patrón Proxy

Para explicar el patrón a través de un ejemplo vamos a usar una implementación «mockeada» de una interfaz que hace un DAO para la clase producto. Tenemos una clase Product, que implementa un POJO y una interfaz ProductDao encargada de definir los métodos de persistencia a implementar para llevar a la base de datos los objetos del negocio para Product.

Implementan la interfaz la clase ProductDaoImpl, que se encarga de la lógica «mockeada» de guardar en la base de datos. Y de la otra parte la clase que funciona como Proxy llamada ProductDaoProxy, que por supuesto también implemeneta la interfaz con el objetivo de que ambos objetos tengan los mismos métodos (Con esto logramos lo mostrado en la figura anterior).

La clase ProductDaoProxy agrega lógica adicional no relacionada con el negocio de la implementación del Dao directamente (en este caso de uso adicionamos logueo). De esta forma logramos desacoplamiento y logramos que ProductDaoImpl cumpla con el principio de responsabilidad única (SRP).

El diagrama de clases para este ejemplo sería:

Ejemplo de Patrón Proxy para ProductDao

Nota: Los métodos marcados en amarillo son los definidos en la interfaz ProductDao.

Llevado a código a modo de ejemplo sería:

Clase Product (Pojo)

package com.sacavix.proxy;

public class Product {
	
	private Long id;
	private String nombre;
	
	public Product(Long id, String nombre) {
		super();
		this.id = id;
		this.nombre = nombre;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getNombre() {
		return nombre;
	}
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	@Override
	public String toString() {
		return "Product [id=" + id + ", nombre=" + nombre + "]";
	}
}

La interfaz ProductDao, encargada de establecer el contrato para los objetos que se creen a partir de la clase principal ProductDaoImpl (que serán proxiados) y para los objetos que se generan desde ProductDaoProxy (el que será el proxy, o que proxea)

package com.sacavix.proxy;

public interface ProductDao {
	public Product findById(Long id);
	public Product save(Product product);
}
package com.sacavix.proxy;

public class ProductDaoImpl implements ProductDao {

	public Product findById(Long id) {
		System.out.println("Finding product with id = " + id);
		return new Product(id, "Sample");
	}
	
	public Product save(Product product) {
		System.out.println("Saving product ...." );
		return product;
	}
}
package com.sacavix.proxy;

public class ProductDaoProxy implements ProductDao {
	
	private final ProductDao productDao;
	
	public ProductDaoProxy(ProductDao productDao) {
		this.productDao = productDao;
	}

	public Product findById(Long id) {
		System.out.println("Logic of proxy before find ...");
		Product p = this.productDao.findById(id);
		System.out.println("Logic of proxy after find ...");
		return p;
	}

	public Product save(Product product) {
		System.out.println("Logic of proxy before save ...");
		Product p = this.productDao.save(product);
		System.out.println("Logic of proxy after save ...");
		return p;
	}
}

El programa de ejemplo muestra un programa principal en el que se ejecuta el código con y sin usar el objeto proxy creado, en el código en GitHub pueden ver el ejemplo y clonarlo para ejecutarlo.

¿ Cuándo usar un patrón Proxy ?

En el ejemplo anterior vimos un caso práctico de caso de uso de aplicación, veamos otros escenarios en los que puede aplicarse:

  • Controlar el acceso a otro objeto (al objeto proxiado).
  • Para procesos de inicialización diferida de un objeto (lazy initialization).
  • Implementación de funciones de logging (como en el ejemplo).
  • Implementar operaciones de facilitación de conexiones de redes.
  • Conteo de referencias a objetos.

El patrón Proxy está presente en la mayoría de los frameworks, en una próxima entrada hablaremos de como lo implementa Spring Framework a partir de las variantes Cglib y JDK Dynamic Proxy.

Puedes ver todo el código del ejemplo en GitHub. Espero te guste el artículo.

Un comentario en «Patrón Proxy: explicación, ejemplo y casos de uso»

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *