Saltar al contenidoJMRG
Todas las entradas

Patrón Prototype: Clonación Eficiente de Objetos

design-patternscreationalprototype

Intención

El patrón Prototype permite crear nuevos objetos copiando una instancia existente (el prototipo), evitando el costo de crear objetos desde cero.

El Problema

Crear objetos complejos desde cero puede ser costoso:

// Crear un documento complejo es costoso
const doc1 = new Document();
doc1.loadTemplate('report');
doc1.applyStyles(defaultStyles);
doc1.initializePlugins(['charts', 'tables']);
// 500ms de inicialización...

// ¿Y si necesito 100 documentos similares?

La Solución: Prototype

classDiagram
    class Prototype {
        <<interface>>
        +clone(): Prototype
    }

    class ConcretePrototype {
        -state: State
        +clone(): ConcretePrototype
    }

    class Client {
        -prototype: Prototype
        +operation()
    }

    Prototype <|.. ConcretePrototype
    Client --> Prototype

Implementación TypeScript

interface Cloneable<T> {
  clone(): T;
}

interface DocumentConfig {
  template: string;
  styles: Record<string, string>;
  plugins: string[];
  metadata: {
    author: string;
    created: Date;
  };
}

class Document implements Cloneable<Document> {
  constructor(private config: DocumentConfig) {}

  clone(): Document {
    // Deep clone del config
    return new Document({
      ...this.config,
      styles: { ...this.config.styles },
      plugins: [...this.config.plugins],
      metadata: {
        ...this.config.metadata,
        created: new Date(), // Nueva fecha para el clon
      },
    });
  }

  setAuthor(author: string): this {
    this.config.metadata.author = author;
    return this;
  }

  getConfig(): DocumentConfig {
    return this.config;
  }
}

// Registro de prototipos
class DocumentRegistry {
  private prototypes = new Map<string, Document>();

  register(name: string, prototype: Document): void {
    this.prototypes.set(name, prototype);
  }

  create(name: string): Document {
    const prototype = this.prototypes.get(name);
    if (!prototype) throw new Error(`Prototype '${name}' not found`);
    return prototype.clone();
  }
}

// Uso
const registry = new DocumentRegistry();

// Registrar prototipos una vez (costoso)
registry.register('report', new Document({
  template: 'report',
  styles: { header: 'bold', body: 'normal' },
  plugins: ['charts', 'tables'],
  metadata: { author: '', created: new Date() },
}));

// Crear múltiples documentos (rápido)
const doc1 = registry.create('report').setAuthor('Alice');
const doc2 = registry.create('report').setAuthor('Bob');

Shallow vs Deep Clone

// Shallow clone - comparte referencias
const shallowClone = { ...original };

// Deep clone - copia todo
const deepClone = structuredClone(original);

// Para objetos con métodos, implementar clone() manualmente

Cuándo Usar

Situación Recomendación
Objetos costosos de crear ✅ Recomendado
Templates o configuraciones base ✅ Recomendado
Cachés de objetos ✅ Recomendado
Objetos simples ❌ new es suficiente

Conclusión

Prototype es ideal cuando la creación desde cero es costosa y puedes reutilizar instancias existentes como punto de partida.