Skip to content
SP StackPractices
intermediate

Patrón Proxy

Proporciona un sustituto o marcador de posición para otro objeto para controlar el acceso a él. Un patrón estructural para control de acceso, carga perezosa y logging.

Temas: design

Patrón Proxy

Visión General

El Patrón Proxy es un patrón de diseño estructural que proporciona un sustituto o marcador de posición para otro objeto. El proxy controla el acceso al sujeto real, agregando una capa de indirección que puede usarse para carga perezosa, control de acceso, caching, logging o monitoreo — sin cambiar el código del sujeto.

Cuándo Usarlo

Usa el Patrón Proxy cuando:

  • Necesitas inicialización perezosa para objetos costosos (crear en primer uso)
  • Quieres controlar derechos de acceso a un objeto (autenticación, autorización)
  • Necesitas cachear resultados de operaciones costosas
  • Quieres registrar o monitorear llamadas a un objeto transparentemente
  • El objeto real es remoto y necesitas un representante local

Solución

Python

from abc import ABC, abstractmethod

class Image(ABC):
    @abstractmethod
    def display(self):
        pass

class RealImage(Image):
    def __init__(self, filename: str):
        self.filename = filename
        self._load_from_disk()

    def _load_from_disk(self):
        print(f"Cargando imagen: {self.filename}")

    def display(self):
        print(f"Mostrando imagen: {self.filename}")

class ImageProxy(Image):
    def __init__(self, filename: str):
        self.filename = filename
        self._real_image = None

    def display(self):
        if self._real_image is None:
            self._real_image = RealImage(self.filename)
        self._real_image.display()

# Uso: el objeto costoso no se carga hasta que se necesita
proxy = ImageProxy("foto.jpg")  # Sin carga
proxy.display()                  # Carga y muestra
proxy.display()                  # Usa el RealImage cacheado

JavaScript

class RealImage {
  constructor(filename) {
    this.filename = filename;
    this.loadFromDisk();
  }

  loadFromDisk() {
    console.log(`Cargando imagen: ${this.filename}`);
  }

  display() {
    console.log(`Mostrando imagen: ${this.filename}`);
  }
}

class ImageProxy {
  constructor(filename) {
    this.filename = filename;
    this.realImage = null;
  }

  display() {
    if (!this.realImage) {
      this.realImage = new RealImage(this.filename);
    }
    this.realImage.display();
  }
}

// Uso
const proxy = new ImageProxy("foto.jpg");
proxy.display(); // Carga perezosa
proxy.display(); // Usa instancia cacheada

Java

public interface Image {
    void display();
}

public class RealImage implements Image {
    private final String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Cargando imagen: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Mostrando imagen: " + filename);
    }
}

public class ImageProxy implements Image {
    private final String filename;
    private RealImage realImage;

    public ImageProxy(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// Uso
Image proxy = new ImageProxy("foto.jpg");
proxy.display(); // Carga perezosa
proxy.display(); // Reutiliza RealImage cacheado

Explicación

El Patrón Proxy involucra tres roles:

  • Interfaz del Sujeto (Image): La interfaz común compartida por el objeto real y el proxy
  • Sujeto Real (RealImage): El objeto real que realiza el trabajo
  • Proxy (ImageProxy): Controla el acceso al sujeto real, agregando comportamiento antes o después de reenviar peticiones

El proxy puede interceptar operaciones para agregar caching, logging, control de acceso o inicialización perezosa transparentemente.

Variantes

VariantePropósitoEjemplo
Proxy VirtualInicialización perezosaCargar imágenes grandes bajo demanda
Proxy de ProtecciónControl de accesoVerificar permisos antes de ejecutar métodos
Proxy de CacheMemoizaciónCachear respuestas de API o resultados computados
Proxy RemotoTransparencia de redStub local para un servicio remoto
Referencia InteligenteConteo de referenciasRastrear uso de objetos para limpieza

Buenas Prácticas

  • Mantén la interfaz del proxy idéntica al sujeto real — los clientes no deben saber que usan un proxy
  • Usa inicialización perezosa solo cuando el objeto real es costoso — de lo contrario, el proxy agrega complejidad innecesaria
  • Maneja la seguridad de hilos en proxies de cache cuando múltiples clientes pueden acceder datos cacheados compartidos
  • Implementa conteo de referencias en proxies inteligentes para gestionar el ciclo de vida de recursos costosos
  • Documenta el comportamiento del proxy para que los llamadores entiendan las características de rendimiento

Errores Comunes

  • Exponer el estado interno del proxy o permitir que los clientes lo eviten para acceder directamente al sujeto real
  • Usar un proxy cuando un simple decorator o referencia directa sería suficiente, agregando indirección innecesaria
  • Olvidar manejar excepciones en el proxy, dejando que los fallos eviten silenciosamente el logging o la lógica de limpieza
  • Implementar proxies de cache sin invalidación de cache, llevando a datos obsoletos
  • No sincronizar el acceso en entornos multihilo, causando condiciones de carrera en la inicialización perezosa

Preguntas Frecuentes

P: ¿Cuál es la diferencia entre Proxy y Decorator? R: Ambos envuelven objetos y agregan comportamiento. Proxy controla el acceso al objeto envuelto (frecuentemente por razones estructurales como carga perezosa o acceso remoto). Decorator agrega responsabilidades dinámicamente, generalmente para mejora funcional. La intención difiere aunque la estructura parezca similar.

P: ¿Puede un proxy envolver otro proxy? R: Sí. Puedes apilar proxies — por ejemplo, un proxy de cache envolviendo un proxy remoto. Cada capa agrega su propio comportamiento. Mantén la pila superficial para evitar stack traces confusos y overhead de rendimiento.

P: ¿Cuándo debería usar un Proxy en lugar de un Factory? R: Usa Factory cuando quieras controlar qué clase se instancia. Usa Proxy cuando quieras controlar el acceso a un objeto ya instanciado o retrasar su creación.