En la última entrada vimos el patrón de diseño Singleton, donde recordemos que se usa principalmente cuando queremos mantener la instancia de un objeto como única, hoy veremos el patrón de diseño Prototype.
Qué son los patrones de diseño?
Los patrones de diseño son soluciones reutilizables y probadas para problemas comunes en el desarrollo de software. Son como «recetas» que los desarrolladores pueden aplicar para resolver desafíos específicos en la arquitectura o el diseño de un sistema, de manera eficiente y con buenas prácticas.
No son fragmentos de código listos para usar, sino guías que describen cómo estructurar el código para abordar ciertos problemas de diseño. Pueden ser implementados en diferentes lenguajes de programación, como Java, Python, C#, entre otros.
Qué es el patrón Prototype?
El patrón Prototype permite crear nuevos objetos copiando o clonando instancias existentes, en lugar de construirlos desde cero. Esto es especialmente útil cuando la creación de un objeto es costosa en términos de tiempo o recursos.
El objetivo principal es crear objetos similares de forma eficiente y sin depender de la implementación específica de sus clases.
Cuándo usar Prototype?
Algunos casos donde Prototype es útil puede ser:
- Costos elevados de creación: Si construir un objeto requiere inicialización compleja o consume muchos recursos.
- Objetos configurables: Cuando necesitas instancias con configuraciones similares pero con ligeras variaciones.
- Reducción de dependencias: Evitar acoplamientos rígidos con el código de construcción del objeto.
- Edición de documentos: Crear versiones similares de un documento base.
- Juegos: Generar personajes o mapas con configuraciones similares.
- Diseño gráfico: Crear múltiples elementos con ligeras variaciones, como formas o colores.
- Prototipos de productos: Duplicar configuraciones para pruebas rápidas.
Cómo implementar Prototype en Java
1. Estructura del patrón
Para usar el patrón Prototype, la clase debe implementar la interfaz Cloneable y sobrescribir el método clone.
Veamoslo en el siguiente ejemplo:
public abstract class Prototipo implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}2. Clase concreta que extiende el Prototype
Luego de tener nuestra clase Prototipo, debemos crear una clase que extenderá de Prototipo, esto con el fin de brindarle la posibilidad de ser clonada.
public class Documento extends Prototipo {
private String tipo;
private String contenido;
public Documento(String tipo) {
this.tipo = tipo;
}
public void setContenido(String contenido) {
this.contenido = contenido;
}
public void imprimir() {
System.out.println("Tipo: " + tipo + " | Contenido: " + contenido);
}
}3. Uso del patrón Prototype
Por último, cada vez que creemos una instancia, podrémos llamar el método .clone() para clonar el objeto previamente creado:
public class Main {
public static void main(String[] args) {
try {
// Crear un prototipo base
Documento documentoOriginal = new Documento("Informe");
documentoOriginal.setContenido("Contenido original del informe.");
// Clonar el prototipo
Documento documentoClonado = (Documento) documentoOriginal.clone();
documentoClonado.setContenido("Contenido modificado del informe.");
// Imprimir ambos documentos
documentoOriginal.imprimir();
documentoClonado.imprimir();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}Salida:
Si vemos la salida, tenemos «Tipo: Informe |» como la base clonada y añadimos el contenido restante.
Tipo: Informe | Contenido: Contenido original del informe. Tipo: Informe | Contenido: Contenido modificado del informe.
Ventajas y desventajas del patrón Prototype
Ventajas:
- Rápida creación de objetos: Clonar un objeto es más rápido que construirlo desde cero.
- Flexible: Permite crear nuevos objetos sin depender de sus constructores.
- Menor acoplamiento: No necesitas conocer la clase exacta del objeto para clonarlo.
Desventajas:
- Complejidad en clases complejas: Si el objeto tiene referencias a otros objetos, la clonación profunda puede complicarse.
- Dependencia de Cloneable: Sobrescribir el método clone puede generar problemas si no se maneja correctamente.
Combinación con Singleton
Prototype a diferencia de Singleton, permite clonar las instancias ya creadas, facilitando la inicialización de las mismas, recordemos que Singleton cuando trabaja en ambientes multhilos, no verifica la instancia en otros hilos a menos que usemos la palabra reservada synchronized, es por ello que ambos podrían trabajar juntos, siempre y cuando Prototype copie instancias creadas por Singleton a otros hilos.
Esto nos podría funcionar en casos como:
- Configuración por hilo: Clonar configuraciones globales del Singleton para personalización por hilo.
- Plantillas de documentos: Obtener copias de una plantilla base (Singleton) para personalizar en cada hilo.
- Contexto de ejecución: Crear copias locales del estado global para cálculos específicos en hilos.
- Recursos gráficos: Generar copias modificables de texturas o modelos base almacenados en el Singleton.
- Simulaciones concurrentes: Clonar prototipos de objetos base del Singleton para cálculos independientes en cada hilo.
De esta manera Singleton actúa como proveedor del prototipo base, y cada hilo trabaja con su propio clon, evitando conflictos y bloqueos.
