Saltar al contenidoJMRG
Todas las entradas

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 --> RealSubject

Virtual 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 recarga

Caching 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.