Patrón Proxy: Control de Acceso y Optimización
design-patternsstructuralproxy

Intención
Proxy proporciona un sustituto o placeholder para otro objeto para controlar el acceso a él. Es útil para lazy loading, caché, control de acceso, y logging.
Tipos de Proxy
classDiagram
class Subject {
<<interface>>
+request()
}
class RealSubject {
+request()
}
class Proxy {
-realSubject: RealSubject
+request()
}
Subject <|.. RealSubject
Subject <|.. Proxy
Proxy --> RealSubjectVirtual Proxy (Lazy Loading)
interface Image {
display(): void;
getSize(): number;
}
class RealImage implements Image {
private data: Buffer;
constructor(private filename: string) {
this.data = this.loadFromDisk(); // Costoso
}
private loadFromDisk(): Buffer {
console.log(`Loading ${this.filename} from disk...`);
return Buffer.alloc(1024 * 1024); // Simular 1MB
}
display(): void {
console.log(`Displaying ${this.filename}`);
}
getSize(): number {
return this.data.length;
}
}
class ImageProxy implements Image {
private realImage: RealImage | null = null;
constructor(private filename: string) {}
private loadIfNeeded(): RealImage {
if (!this.realImage) {
this.realImage = new RealImage(this.filename);
}
return this.realImage;
}
display(): void {
this.loadIfNeeded().display();
}
getSize(): number {
return this.loadIfNeeded().getSize();
}
}
// Uso
const images = [
new ImageProxy('photo1.jpg'),
new ImageProxy('photo2.jpg'),
new ImageProxy('photo3.jpg'),
];
// Solo se carga cuando se usa
images[0].display(); // Carga photo1.jpg
images[0].display(); // Ya cargada, no recargaCaching Proxy
interface ApiClient {
fetch(endpoint: string): Promise<unknown>;
}
class CachingProxy implements ApiClient {
private cache = new Map<string, { data: unknown; expires: number }>();
constructor(
private client: ApiClient,
private ttl: number = 60000
) {}
async fetch(endpoint: string): Promise<unknown> {
const cached = this.cache.get(endpoint);
if (cached && cached.expires > Date.now()) {
console.log(`Cache HIT: ${endpoint}`);
return cached.data;
}
console.log(`Cache MISS: ${endpoint}`);
const data = await this.client.fetch(endpoint);
this.cache.set(endpoint, {
data,
expires: Date.now() + this.ttl,
});
return data;
}
}Protection Proxy
interface Document {
read(): string;
write(content: string): void;
}
class ProtectedDocument implements Document {
constructor(
private document: Document,
private userRole: string
) {}
read(): string {
return this.document.read();
}
write(content: string): void {
if (this.userRole !== 'admin' && this.userRole !== 'editor') {
throw new Error('Access denied: insufficient permissions');
}
this.document.write(content);
}
}Cuándo Usar
| Tipo | Caso de Uso |
|---|---|
| Virtual Proxy | Lazy loading de recursos pesados |
| Caching Proxy | Reducir llamadas a APIs/DB |
| Protection Proxy | Control de acceso |
| Logging Proxy | Auditoría y métricas |
Conclusión
Proxy es versátil para añadir comportamiento transversal sin modificar el objeto original.