Abby's Digital Cafe

Patrón creaciónal - Prototype

Introducción al patrón Prototype

El patrón Prototype es un patrón creacional que permite crear nuevos objetos clonando instancias existentes (prototipos) en lugar de construirlos desde cero. Es especialmente útil cuando la creación del objeto es costosa, cuando queremos evitar cadenas de constructores complejas o cuando necesitamos copias configuradas en tiempo de ejecución. En esta guía verás ejemplos concretos en TypeScript y Java, pasos para aplicar el patrón y preguntas frecuentes con respuestas prácticas.

¿Cuándo usar Prototype?

  • Cuando crear una instancia es costoso (operaciones I/O, cálculos, inicialización compleja).
  • Cuando se necesita duplicar objetos con configuraciones similares en tiempo de ejecución.
  • Cuando quieres separar la lógica de clonación (deep vs shallow) de la lógica de negocio.
  • Cuando buscas flexibilidad para registrar y clonar prototipos dinámicamente (registro/registry).

Implementación en TypeScript (ejemplo práctico)

En TypeScript es común implementar una interfaz con un método clone() que devuelva la copia. Punto importante: suele ser necesario decidir si la clonación será superficial (shallow) o profunda (deep). A continuación un ejemplo que muestra ambas variantes y un registry para reutilizar prototipos:

[typescript]
interface Prototype<T> { clone(deep?: boolean): T; } class Document implements Prototype<Document> { constructor(public title: string, public content: any) {} // clonación: por defecto shallow; si deep=true usamos JSON (simple deep clone) clone(deep = false): Document { if (!deep) { // shallow copy: new referencia para objeto raíz pero referencias internas iguales return new Document(this.title, this.content); } // deep copy (ejemplo simple): no válido para funciones o fechas complejas const copied = JSON.parse(JSON.stringify(this)); return new Document(copied.title, copied.content); } } // Registry de prototipos class PrototypeRegistry { private prototypes = new Map<string, Prototype<any>>(); register(key: string, proto: Prototype<any>) { this.prototypes.set(key, proto); } clone<T>(key: string, deep = false): T | null { const p = this.prototypes.get(key) as Prototype<T> | undefined; return p ? p.clone(deep) as T : null; } } // Uso const registry = new PrototypeRegistry(); const docPrototype = new Document('Plantilla', { sections: ['intro', 'body'] }); registry.register('doc:basic', docPrototype); const copy1 = registry.clone<Document>('doc:basic'); // shallow const copy2 = registry.clone<Document>('doc:basic', true); // deep

Notas prácticas: usar JSON.parse/JSON.stringify para deep clone es rápido y sencillo, pero tiene limitaciones (no copia funciones, fechas, Map/Set ni referencias cíclicas). Para casos complejos usa librerías como lodash.clonedeep o una implementación personalizada.

Implementación en Java (ejemplo práctico)

En Java hay dos enfoques comunes: implementar Cloneable y sobrescribir clone() o usar constructores de copia (copy constructor). El enfoque del constructor de copia es más seguro y claro; el método clone() heredado de Object tiene peculiaridades y excepciones.

[java]
public class Document implements Cloneable { private String title; private List<String> sections; public Document(String title, List<String> sections){ this.title = title; this.sections = sections; } // Copy constructor (forma recomendada) public Document(Document other){ this.title = other.title; this.sections = new ArrayList<>(other.sections); // shallow copy de lista } // Método clone (puede lanzar CloneNotSupportedException) @Override public Document clone(){ try{ Document copy = (Document) super.clone(); copy.sections = new ArrayList<>(this.sections); // clonar colección return copy; } catch(CloneNotSupportedException e){ throw new AssertionError(); } } } // Uso Document proto = new Document("Plantilla", Arrays.asList("intro","body")); Document copy = new Document(proto);

Consejo: si tu objeto contiene referencias a objetos mutables, asegúrate de clonar esas referencias (deep copy) cuando sea necesario para evitar efectos colaterales.

Pasos prácticos para aplicar Prototype

  1. Identificar si la creación de objetos es costosa o si necesitas duplicados configurados.
  2. Definir una interfaz/contrato que incluya un método clone() o usar copy constructors en Java.
  3. Decidir política de clonación: shallow vs deep. Documentarlo claramente.
  4. Implementar prototipos y, opcionalmente, un registry para gestionar instancias base reutilizables.
  5. Escribir tests que verifiquen que las copias no comparten referencias mutables cuando no deben.

¿Prototype reemplaza a Factory o Builder?

No exactamente. Prototype se centra en clonar instancias existentes; Factory/Builder crean nuevas instancias a partir de parámetros. Usa Prototype cuando la clonación de una instancia existente es la forma más natural o eficiente. Usa Factory/Builder cuando la construcción paso a paso o parametrizada es prioritaria.

Preguntas frecuentes (Q&A)

¿Cuál es la diferencia entre shallow y deep clone?

Shallow clone copia las referencias de los campos (las colecciones y objetos internos siguen apuntando a los mismos objetos). Deep clone crea copias independientes de las estructuras internas. Elige shallow por rendimiento si los objetos internos son inmutables; elige deep cuando necesites independencia total.

¿Es seguro usar Object.clone() en Java?

El método clone() de Java funciona, pero tiene trampas: dependes de Cloneable, super.clone() y manejo de CloneNotSupportedException. Muchos prefieren constructores de copia o patrones como Builder para mayor claridad y control.

¿Cuándo NO usar Prototype?

Evítalo si la creación es barata o cuando la lógica de inicialización depende de parámetros externos que no vienen del prototipo base. Tampoco lo uses si la clonación profunda es tan compleja que es más simple construir desde cero.

Comparativa rápida entre patrones creacionales
PatrónVentajaCuándo usar
PrototypeRápido para duplicar objetos configuradosObjetos costosos de crear o plantillas runtime
FactoryCentraliza creación parametrizadaCuando instancias varían según parámetros
BuilderConstrucción paso a paso y legibleObjetos complejos con muchas opciones

Conclusión: Prototype es una herramienta poderosa cuando se necesita clonar objetos con eficiencia y flexibilidad. Implementarlo bien implica decidir la estrategia de clonación, documentarla y probarla.

Comentarios