El Prototype permite la creación de nuevos objetos clonando un objeto existente, conocido como el prototipo, evitando la necesidad de conocer los detalles de cómo se crean estos objetos.
Tenemos un objeto con algunos estados posibles, creamos prototipos de cada uno y los clonamos, evitando tener que pasar muchos parámetros, saber qué valores van en cada uno, o sea, facilitando la creación.
La necesidad de duplicar objetos existentes, con comportamiento o estado similar, o cuando la creación directa es costosa (en términos de recursos o tiempo).
La solución que propone el Prototype es:
Con esta solución, un cliente le pide al prototipo que se clone.
Este patrón es recomendable cuando:
Eficiencia en la creación de objetos: Clonar generalmente es más eficiente que crear un objeto nuevo.
Oculta la complejidad de creación: El cliente no necesita saber cómo se construye el objeto.
Agregar y eliminar objetos en tiempo de ejecución: Instalando o eliminando prototipos en tiempo de ejecución, algo que da flexibilidad.
Clonaciones complejas: Objetos complejos, objetos con otros objetos adentro (clonación superficial vs profunda), o con referencias circulares, pueden traer problemas.
Acoplamiento con clases concretas: El patrón requiere que las clases concretas y cada subclase implementen la operación de clonación, algo que puede ser difícil si las clases ya existen.
Estamos desarrollando un sistema de gráficos y necesitamos crear múltiples instancias de formas geométricas configurables, como círculos o rectángulos.
Los clientes deben poder crear de forma simple y eficiente figuras geométricas, como círculos o rectángulos, sin necesidad de saber cómo se construyen estos objetos.
Debemos tener en cuenta que las formas, en un sistema gráfico, suelen tener configuraciones y estados internos que pueden ser complejos y costosos de replicar.
Implementamos un Prototype, porque permite crear nuevas instancias de las formas de manera eficiente, clonando un objeto existente en lugar de construir uno nuevo.
Si más adelante se necesitan nuevas formas o variantes, es fácil agregar nuevas clases que implementen el prototipo sin alterar el resto del sistema.
Creamos la interfaz Shape que contiene la operación de clonación.
Circle y Rectangle son los productos concretos, implementan la interfaz Shape, saben cómo clonarse.
GraphicTool es la clase que usa los prototipos y puede crear nuevas formas usando el método de clonación.
Codificamos en Java lo que preparamos en el diagrama.
Definimos la interfaz prototipo de la forma:
interface Shape {
Shape clone();
}
Implementamos un prototipo concreto para círculo y rectángulo:
class Circle implements Shape {
private int radius;
public Circle(int radius) {
this.radius = radius;
}
@Override
public Circle clone() {
return new Circle(this.radius);
}
// Métodos adicionales…
}
class Rectangle implements Shape {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public Rectangle clone() {
return new Rectangle(this.width, this.height);
}
// Métodos adicionales…
}
El código cliente, usa los prototipos para crear formas:
class GraphicTool {
private Shape shape;
public GraphicTool(Shape shape) {
this.shape = shape;
}
public Shape createShape() {
return shape.clone();
}
}
Los participantes que vimos antes son: Prototype, ConcretePrototype, Client:
Necesitábamos crear nuevas instancias de objetos complejos de manera eficiente, para esto usamos un Prototype. En lugar de crear un objeto de cero cada vez, creamos un prototipo y lo clonamos, lo cual es sumamente útil cuando la creación de un objeto es una operación costosa.
Además, este enfoque mantiene nuestro código flexible y fácil de extender a nuevas formas geométricas.
Suele ser usado con Prototype para crear objetos.
Pueden usarse juntos, una fábrica abstracta puede almacenar un conjunto de prototipos a partir de los cuales clonar y devolver objetos producto.
Los diseños que usan patrones Composite y Decorator suelen beneficiarse también del Prototype.