Saltar al contenido
DevPedia

Patrones estructuralesGuía 15 de 28

Proxy

Cómo controlar el acceso a un objeto mediante un intermediario con la misma interfaz y en qué casos resulta útil.

Actualizado 6 min de lectura

Proxy es un patrón estructural que pone un objeto sustituto delante de otro para controlar el acceso.

El problema

Cada producto de AndesShop tiene varias fotos en alta resolución. Cargarlas desde el almacenamiento 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 todos 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 pospone 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), cachear un resultado costoso, o hablar con un objeto que vive en otro proceso (remote proxy).

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
El cliente sigue hablando con la misma interfaz: carga diferida, caché o permisos quedan detrás del proxyAgrega una capa de indirección que puede complicar el seguimiento del código
Pospone trabajo costoso hasta el primer uso realLa respuesta puede llegar con latencia extra la primera vez que se resuelve el objeto real
Sirve también cuando el objeto real todavía no existe o vive 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.

Compartir esta guía

Buscá por concepto, patrón o práctica.