El Proxy es un sustituto que conserva la interfaz de otro objeto y controla el acceso a él. Puede retrasar su creación, comprobar permisos o representar un recurso remoto.
Cuando vais a comprar, usáis la tarjeta (el Proxy). El vendedor acepta la tarjeta como si fuera dinero real. Por detrás, la tarjeta valida si tenéis fondos, procesa la transacción y mueve el dinero real. Si no tenéis fondos, la tarjeta bloquea la operación sin tener que ir al banco físicamente.
En desarrollo de software, el patrón Proxy funciona igual: es un objeto que actúa como sustituto o marcador de posición de otro objeto para controlar el acceso a él.
¿Por qué necesitamos un intermediario?
Podríais pensar: “¿Por qué no llamo a la clase real y ya está?”. Hay varias razones de peso:
- Coste (Lazy Loading): El objeto real es muy pesado (ej: una imagen de 500MB o una base de datos entera). No queremos cargarlo en memoria hasta que sea estrictamente necesario.
- Seguridad (Protection Proxy): Queremos comprobar si el usuario tiene permisos de “Admin” antes de dejarle ejecutar un método sensible.
- Ubicación (Remote Proxy): El objeto real está en otro servidor (muy común en servicios web). El Proxy local hace que parezca que el objeto está aquí mismo.
Implementación en C#: Virtual Proxy (Lazy Loading)
El caso de uso más común en aplicaciones de escritorio y web es la Carga Diferida.
Imagina una lista de videos de YouTube. Queremos mostrar el título de 50 videos. Si cargamos el video completo (bytes) de los 50 al arrancar la app, el ordenador explotará.
Usaremos un Proxy para cargar el video solo cuando el usuario le dé al Play.
La Interfaz (Subject)
public interface IVideo
{
void Reproducir();
}El Objeto Real (Costoso)
Simulamos que cargar este objeto tarda tiempo y consume memoria.
public class VideoReal : IVideo
{
private string _titulo;
public VideoReal(string titulo)
{
_titulo = titulo;
CargarDesdeDisco(); // ⚠️ Operación costosa en el constructor
}
private void CargarDesdeDisco()
{
Console.WriteLine($"⏳ Cargando video '{_titulo}' de 500MB en memoria...");
Thread.Sleep(2000); // Simulamos retardo de 2 segundos
}
public void Reproducir()
{
Console.WriteLine($"▶️ Reproduciendo video '{_titulo}'.");
}
}El Proxy (Ligero)
El Proxy es rápido de crear. Solo tiene el título (un simple string). Solo crea el VideoReal si alguien llama a Reproducir().
public class VideoProxy : IVideo
{
private VideoReal _videoReal;
private string _titulo;
public VideoProxy(string titulo)
{
_titulo = titulo;
}
public void Reproducir()
{
// LAZY LOADING: Solo creamos el objeto real si no existe
if (_videoReal == null)
{
Console.WriteLine("Proxy: Inicializando el objeto real bajo demanda...");
_videoReal = new VideoReal(_titulo);
}
// Delegamos la acción al objeto real
_videoReal.Reproducir();
}
}El Cliente
class Program
{
static void Main(string[] args)
{
Console.WriteLine("--- App Iniciada ---");
// Creamos el Proxy. ¡Es instantáneo! No se carga nada pesado todavía.
IVideo miVideo = new VideoProxy("Tutorial de Patrones.mp4");
Console.WriteLine("Objeto video creado (pero no cargado).");
Console.WriteLine("Haciendo otras cosas...");
// ... pasa el tiempo ...
Console.WriteLine("\n--- El usuario pulsa PLAY ---");
// AQUÍ es donde ocurre la carga pesada
miVideo.Reproducir();
Console.WriteLine("\n--- El usuario pulsa PLAY otra vez ---");
// La segunda vez ya no carga, usa el que ya tiene en caché
miVideo.Reproducir();
}
}Salida:
--- App Iniciada ---
Objeto video creado (pero no cargado).
Haciendo otras cosas...
--- El usuario pulsa PLAY ---
Proxy: Inicializando el objeto real bajo demanda...
⏳ Cargando video 'Tutorial de Patrones.mp4' de 500MB en memoria...
▶️ Reproduciendo video 'Tutorial de Patrones.mp4'.
--- El usuario pulsa PLAY otra vez ---
▶️ Reproduciendo video 'Tutorial de Patrones.mp4'.Diferencias Clave con otros Patrones
Conviene no confundirse, porque estructuralmente se parecen mucho:
| Patrón | Estructura | Intención |
|---|---|---|
| Adapter | Envuelve un objeto | Cambiar su interfaz para que sea compatible. |
| Decorator | Envuelve un objeto | Añadir funcionalidad o comportamiento extra. |
| Proxy | Envuelve un objeto | Controlar el acceso al objeto (misma interfaz). |
Otros tipos de Proxy
Aparte del Virtual Proxy (Lazy Loading) que acabamos de ver, existen otros muy útiles:
- Protection Proxy (Seguridad): Comprueba permisos.
public void Reproducir() {
if (usuario.EsAdmin) _real.Reproducir();
else throw new UnauthorizedAccessException();
}- Logging Proxy: Registra cada vez que se usa el objeto. Muy útil para depuración.
- Caching Proxy: Guarda el resultado de operaciones costosas y las devuelve si se repiten (muy usado en servidores web).
El patrón Proxy es el “portero de discoteca” de nuestros objetos. Decide quién pasa y cuándo pasa. Es una herramienta indispensable para mejorar el rendimiento de aplicaciones grandes y proteger componentes sensibles.