Problema
Controlar el acceso a otro objeto proporcionando un sustituto o placeholder que puede agregar funcionalidad adicional.
Propósito
Proporcionar un sustituto o placeholder para otro objeto para controlar el acceso a él. Permite agregar funcionalidad como lazy loading, caching, logging, control de acceso o validación sin modificar el objeto original.
Casos de uso comunes
- Lazy loading de recursos costosos
- Control de acceso y seguridad
- Caching de resultados
- Logging y auditoría
- Validación antes de operaciones
- Conexiones remotas (Remote Proxy)
¿Quién es quién en Proxy?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
Subject | Interfaz común para Proxy y RealSubject | Image - operaciones que ambos implementan |
“Reproductor de video” (interfaz) |
RealSubject | Objeto real que hace el trabajo pesado | HighResolutionImage - carga y muestra imagen real |
Archivo de video real (pesado, en disco) |
Proxy | Sustituto que controla acceso y agrega funcionalidad | ImageProxy - lazy loading, caching, control |
Thumbnail/preview (ligero, controla carga) |
Client | Usa el servicio sin saber si es Proxy o Real | ImageViewer - no sabe si imagen está cargada |
Persona que quiere ver película |
Diagrama
classDiagram
namespace ProxyPattern {
class Subject {
<<interface>>
+request()
}
class RealSubject {
+request()
}
class Proxy {
-realSubject: RealSubject
+request()
-checkAccess() boolean
-logAccess()
}
class Client {
+clientCode(Subject)
}
}
Subject <|.. RealSubject
Subject <|.. Proxy
Proxy --> RealSubject
Client --> Subject
Tipos de Proxy
graph TB
A[Proxy Pattern] --> B[Virtual Proxy]
A --> C[Protection Proxy]
A --> D[Remote Proxy]
A --> E[Caching Proxy]
A --> F[Smart Reference]
B --> B1["Lazy loading<br/>Objetos costosos"]
C --> C1["Control de acceso<br/>Autenticación"]
D --> D1["Objetos remotos<br/>RPC, Web Services"]
E --> E1["Cache de resultados<br/>Optimización"]
F --> F1["Referencias inteligentes<br/>Conteo, logging"]
Ejemplo práctico - Virtual Proxy
classDiagram
namespace VirtualProxyExample {
class Image {
<<interface>>
+display()
+getInfo() String
}
class HighResolutionImage {
-filename: String
-imageData: byte[]
+display()
+getInfo() String
-loadImageFromDisk()
}
class ImageProxy {
-filename: String
-realImage: HighResolutionImage
+display()
+getInfo() String
}
class ImageViewer {
-images: List~Image~
+viewImage(index)
}
}
Image <|.. HighResolutionImage
Image <|.. ImageProxy
ImageProxy --> HighResolutionImage
ImageViewer --> Image
Flujo de Virtual Proxy
sequenceDiagram
participant Client
participant ImageProxy
participant RealImage as HighResolutionImage
Client->>ImageProxy: new ImageProxy("photo.jpg")
Note over ImageProxy: Proxy creado, imagen NO cargada
Client->>ImageProxy: getInfo()
ImageProxy-->>Client: "ImageProxy: photo.jpg (not loaded)"
Client->>ImageProxy: display()
Note over ImageProxy: Primera llamada - lazy loading
ImageProxy->>RealImage: new HighResolutionImage("photo.jpg")
Note over RealImage: Carga costosa desde disco
RealImage-->>ImageProxy: imagen cargada
ImageProxy->>RealImage: display()
RealImage-->>Client: imagen mostrada
Client->>ImageProxy: display()
Note over ImageProxy: Llamadas posteriores - sin carga
ImageProxy->>RealImage: display()
RealImage-->>Client: imagen mostrada
Ejemplo práctico - Protection Proxy
classDiagram
namespace ProtectionProxyExample {
class BankAccount {
<<interface>>
+deposit(amount)
+withdraw(amount)
+getBalance() double
}
class RealBankAccount {
-accountNumber: String
-balance: double
+deposit(amount)
+withdraw(amount)
+getBalance() double
}
class SecureBankAccountProxy {
-realAccount: RealBankAccount
-currentUser: String
-accountOwner: String
+deposit(amount)
+withdraw(amount)
+getBalance() double
-hasAccess() boolean
-logAccess(operation)
}
}
BankAccount <|.. RealBankAccount
BankAccount <|.. SecureBankAccountProxy
SecureBankAccountProxy --> RealBankAccount
Proxy vs Facade vs Adapter
Proxy:
• MISMA interfaz que el objeto real
• Controla ACCESO al objeto (cuándo, cómo)
• Relación 1:1 (un proxy por objeto)
• Ejemplo: ImageProxy controla cuándo cargar HighResolutionImage
Facade:
• NUEVA interfaz simplificada
• Coordina MÚTIPLES subsistemas
• Relación 1:N (una facade para muchos objetos)
• Ejemplo: HomeTheaterFacade coordina audio + video + luces
Adapter:
• CONVIERTE una interfaz en otra
• Hace compatibles interfaces incompatibles
• Relación 1:1 (adaptador por objeto incompatible)
• Ejemplo: XMLAdapter convierte XML a JSON
¿Por qué ambos implementan Subject?
// Cliente solo conoce la interfaz
Subject image = getImage(); // Puede ser Proxy o RealSubject
image.display(); // Cliente no sabe cuál es
// Proxy controla el acceso
class ImageProxy implements Subject {
public void display() {
if (hasPermission()) { // Control de acceso
if (realImage == null) { // Lazy loading
realImage = new HighResolutionImage();
}
realImage.display(); // Delega al objeto real
}
}
}
Ventajas
- Control: Controla acceso al objeto real
- Optimización: Lazy loading, caching, etc.
- Transparencia: Cliente no sabe que está usando un proxy
- Funcionalidad adicional: Logging, validación, seguridad sin modificar el objeto original
Desventajas
- Complejidad: Introduce una capa adicional de abstracción
- Latencia: Puede introducir overhead mínimo
- Mantenimiento: Cambios en el subject pueden requerir cambios en el proxy
- Memoria: El proxy mantiene referencias adicionales
Cuándo usar
- Lazy loading: Cuando la creación del objeto es costosa
- Control de acceso: Cuando necesitas verificar permisos
- Caching: Cuando quieres cachear resultados costosos
- Logging: Cuando necesitas registrar accesos
- Validación: Cuando necesitas validar antes de operaciones
Cuándo NO usar
- El acceso directo al objeto es suficiente
- No necesitas funcionalidad adicional
- La complejidad adicional no se justifica
- El overhead del proxy es inaceptable