El patrón de diseño de creación Prototype se utiliza para crear objetos a partir de otros objetos, en lugar de crearlos desde cero. Este enfoque se basa en la clonación de un objeto existente para crear uno nuevo. Es decir, se utiliza un objeto existente como prototipo y se crea un nuevo objeto a partir de él. Esto puede ser útil cuando la creación de un objeto es costosa en términos de recursos, tiempo o complejidad.
El patrón Prototype se compone de dos partes principales: el objeto prototipo y el objeto cliente. El objeto prototipo es el objeto existente que se utilizará como base para la creación de nuevos objetos. El objeto cliente es el objeto que utiliza el prototipo para crear nuevos objetos.
Este patrón Prototype se implementa mediante la definición de una interfaz de prototipo que especifica los métodos necesarios para clonar el objeto. Luego se crean las clases concretas que implementan esta interfaz de prototipo y proporcionan su propia implementación del método de clonación para crear objetos. Finalmente, el objeto cliente utiliza el método de clonación para crear nuevos objetos a partir del prototipo.
Ejemplos
Aquí hay un ejemplo de cómo se podría implementar el patrón Prototype en Java:
public interface Shape extends Cloneable {
public void draw();
public Shape clone();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle.");
}
@Override
public Shape clone() {
return new Rectangle();
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle.");
}
@Override
public Shape clone() {
return new Circle();
}
}
public class ShapeCache {
private static HashMap<String, Shape> shapeMap = new HashMap<>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
public static void loadCache() {
Rectangle rectangle = new Rectangle();
rectangle.setId("1");
shapeMap.put(rectangle.getId(), rectangle);
Circle circle = new Circle();
circle.setId("2");
shapeMap.put(circle.getId(), circle);
}
}
public class Main {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape1 = ShapeCache.getShape("1");
System.out.println("Shape: " + clonedShape1.getType());
clonedShape1.draw();
Shape clonedShape2 = ShapeCache.getShape("2");
System.out.println("Shape: " + clonedShape2.getType());
clonedShape2.draw();
}
}
En este ejemplo, la interfaz Shape
define los métodos necesarios para clonar el objeto. Las clases Rectangle
y Circle
implementan esta interfaz y proporcionan su propia implementación del método de clonación para crear nuevos objetos.
La clase ShapeCache
se utiliza como una especie de caché para almacenar los objetos existentes que se utilizarán como prototipos. La clase Main
es el objeto cliente que utiliza los prototipos para crear nuevos objetos.
En Python, este patrón de diseño podría verse de la siguiente manera:
import copy
class Shape:
def __init__(self):
self.id = None
self.type = None
def clone(self):
pass
class Rectangle(Shape):
def __init__(self):
super().__init__()
self.type = "Rectangle"
def clone(self):
return copy.copy(self)
class Circle(Shape):
def __init__(self):
super().__init__()
self.type = "Circle"
def clone(self):
return copy.copy(self)
class ShapeCache:
__shapes = {}
@staticmethod
def get_shape(shape_id):
shape = ShapeCache.__shapes.get(shape_id)
return shape.clone()
@staticmethod
def load_cache():
rect = Rectangle()
rect.id = "1"
ShapeCache.__shapes[rect.id] = rect
circle = Circle()
circle.id = "2"
ShapeCache.__shapes[circle.id] = circle
El mismo ejemplo en C#, se ve así:
using System;
using System.Collections.Generic;
public abstract class Shape
{
public int Id { get; set; }
public string Type { get; set; }
public abstract Shape Clone();
}
public class Rectangle : Shape
{
public Rectangle()
{
Type = "Rectangle";
}
public override Shape Clone()
{
return (Shape)MemberwiseClone();
}
}
public class Circle : Shape
{
public Circle()
{
Type = "Circle";
}
public override Shape Clone()
{
return (Shape)MemberwiseClone();
}
}
public static class ShapeCache
{
private static readonly Dictionary<int, Shape> Shapes = new Dictionary<int, Shape>();
public static Shape GetShape(int shapeId)
{
Shapes.TryGetValue(shapeId, out var shape);
return shape?.Clone();
}
public static void LoadCache()
{
var rect = new Rectangle { Id = 1 };
Shapes[rect.Id] = rect;
var circle = new Circle { Id = 2 };
Shapes[circle.Id] = circle;
}
}
Y por último, el mismo concepto en Go.
package main
import (
"fmt"
)
type Shape interface {
Clone() Shape
}
type Rectangle struct {
Id int
Type string
}
func (r *Rectangle) Clone() Shape {
return &Rectangle{Id: r.Id, Type: r.Type}
}
type Circle struct {
Id int
Type string
}
func (c *Circle) Clone() Shape {
return &Circle{Id: c.Id, Type: c.Type}
}
type ShapeCache struct {
shapes map[int]Shape
}
func (c *ShapeCache) GetShape(shapeId int) Shape {
shape := c.shapes[shapeId]
return shape.Clone()
}
func (c *ShapeCache) LoadCache() {
c.shapes = make(map[int]Shape)
rect := &Rectangle{Id: 1, Type: "Rectangle"}
c.shapes[rect.Id] = rect
circle := &Circle{Id: 2, Type: "Circle"}
c.shapes[circle.Id] = circle
}
func main() {
cache := ShapeCache{}
cache.LoadCache()
rectClone := cache.GetShape(1)
fmt.Printf("Shape: %T\n", rectClone)
fmt.Printf("Shape Id: %d\n", rectClone.(*Rectangle).Id)
circleClone := cache.GetShape(2
Beneficios
-
- Reducción de la complejidad del código
- el uso del patrón Prototype permite reducir la complejidad del código, ya que elimina la necesidad de escribir múltiples clases para cada objeto que se desea crear. En su lugar, se crea una sola clase prototipo y se clona para obtener nuevas instancias.
-
- Flexibilidad
- el patrón Prototype ofrece una gran flexibilidad en la creación de objetos. Los clientes pueden crear objetos personalizados clonando un objeto prototipo existente y modificando sus propiedades. Esto hace que sea fácil adaptar los objetos existentes para satisfacer nuevas necesidades.
-
- Mejora del rendimiento
- el uso del patrón Prototype puede mejorar el rendimiento de una aplicación, ya que clonar un objeto existente es más eficiente que crear uno nuevo desde cero. Esto es especialmente importante en situaciones donde se necesita crear muchos objetos similares, ya que el clonado es mucho más rápido que la creación de nuevos objetos.
Entonces, el patrón de diseño Prototype es una técnica útil para crear objetos complejos y personalizados. Al permitir la creación de nuevos objetos a partir de objetos existentes, se puede reducir la complejidad del código, mejorar la flexibilidad y el rendimiento de la aplicación. Además, este patrón también se puede utilizar para evitar la duplicación de código en situaciones en las que se necesitan objetos similares.
Comentarios