Saltar al contenido

Proxy

Un patrón estructural: un objeto que ocupa el lugar de otro y decide cuándo y cómo se accede a él.

6 min. de lectura

Proxy es un patrón estructural que provee un objeto sustituto para controlar el acceso a otro objeto.

El problema

Cada producto de AndesShop tiene varias fotos en alta resolución. Cargar esas imágenes desde el storage en la nube es relativamente lento, y la mayoría de las veces ni siquiera hace falta: en una lista de resultados de búsqueda, se muestran cientos de productos, pero el usuario solo llega a ver en pantalla una fracción de ellos antes de hacer scroll. Cargar las imágenes de los cientos de productos por adelantado, “por las dudas”, desperdicia tiempo y ancho de banda.

La solución

Proxy introduce un objeto que implementa la misma interfaz que el objeto real (ProductImage), pero que retrasa el trabajo costoso hasta el momento en que realmente hace falta. El código cliente interactúa siempre con la misma interfaz, sin necesidad de saber si del otro lado está la imagen real o un sustituto que todavía no la cargó.

Estructura de Proxy: el proxy comparte la interfaz del objeto real y controla cuándo se lo crea. El cliente no nota la diferencia.

Ejemplo en Java

interface ProductImage {
    String display();
}

// El objeto real: cargarlo es costoso
class RealProductImage implements ProductImage {
    private final String url;

    public RealProductImage(String url) {
        this.url = url;
        loadFromCloudStorage(); // trabajo costoso, ocurre al construir el objeto
    }

    private void loadFromCloudStorage() {
        System.out.println("Loading image from " + url + "...");
    }

    public String display() {
        return "Displaying image: " + url;
    }
}

// El Proxy: pospone la carga hasta el primer uso real
class LazyProductImageProxy implements ProductImage {
    private final String url;
    private RealProductImage real;

    public LazyProductImageProxy(String url) {
        this.url = url; // liviano: no carga nada todavía
    }

    public String display() {
        if (real == null) {
            real = new RealProductImage(url); // recién acá se paga el costo
        }
        return real.display();
    }
}
// Client code: crear el proxy es barato, sin importar cuántos productos haya en la lista
List<ProductImage> searchResults = skus.stream()
    .map(sku -> new LazyProductImageProxy(imageUrlFor(sku)))
    .toList();

// La imagen real recién se carga cuando el usuario efectivamente la ve
searchResults.get(0).display();

Cuándo usarlo

  • Cuando necesitás posponer la creación de un objeto costoso hasta el momento en que realmente se necesita (virtual proxy).
  • Cuando necesitás controlar el acceso a un objeto — verificar permisos antes de dejar pasar la llamada (protection proxy), o loguear cada acceso sin modificar el objeto real.

Cuándo evitarlo

Si el objeto no es costoso de crear ni necesita control de acceso, un Proxy solo agrega una capa de indirección sin ningún beneficio real.

Ventajas y desventajas

VentajasDesventajas
Permite controlar el objeto real sin que el código cliente lo noteAgrega una capa de indirección que puede complicar el seguimiento del código
Puede posponer trabajo costoso hasta que realmente haga faltaLa respuesta puede llegar con latencia extra la primera vez que se resuelve el objeto real
Funciona aunque el objeto real todavía no exista o esté en otro proceso

Relación con otros patrones

  • Comparte estructura con Decorator (ambos envuelven un objeto detrás de su misma interfaz), pero con intención distinta: Decorator suma comportamiento, Proxy controla acceso.
  • Se diferencia de Adapter en que Proxy mantiene la misma interfaz que el objeto que envuelve; Adapter la cambia.

Te sirvió, compartilo

// ¿te sirvió?
// compartilo