Problema

Crear objetos clonando instancias existentes cuando la creación directa es costosa o compleja.

Propósito

Especificar los tipos de objetos a crear usando una instancia prototípica, y crear nuevos objetos copiando este prototipo. Evita el costo de creación desde cero cuando la clonación es más eficiente.

Casos de uso comunes

  • Configuraciones de juegos (personajes, niveles)
  • Templates de documentos o emails
  • Objetos con inicialización costosa
  • Caches de objetos complejos
  • Sistemas con muchas variaciones de objetos similares

¿Quién es quién en Prototype?

Actor Lo que realmente es Ejemplo Analogía
Prototype Interfaz que define clone() GameCharacter - define cómo copiarse “Molde” (interfaz)
ConcretePrototype Saben cómo clonarse correctamente Warrior, Mage - implementan clonación Molde maestro (configuración perfecta)
PrototypeRegistry Catálogo que almacena prototipos CharacterRegistry - “Guerrero nivel 10” Biblioteca de moldes
Client Clona en lugar de crear desde cero GameSession - quiere personaje personalizado Jugador (quiere personaje similar)

Diagrama

classDiagram
    namespace PrototypePattern {
        class Prototype {
            <<interface>>
            +clone() Prototype
        }
        
        class ConcretePrototype1 {
            -field1: String
            -field2: int
            +clone() ConcretePrototype1
            +setField1(value)
            +setField2(value)
        }
        
        class ConcretePrototype2 {
            -fieldA: String
            -fieldB: List~String~
            +clone() ConcretePrototype2
            +setFieldA(value)
            +setFieldB(value)
        }
        
        class PrototypeRegistry {
            -prototypes: Map~String, Prototype~
            +register(key, prototype)
            +getPrototype(key) Prototype
        }
        
        class Client {
            +makePrototype(key) Prototype
        }
    }
    
    Prototype <|.. ConcretePrototype1
    Prototype <|.. ConcretePrototype2
    PrototypeRegistry --> Prototype
    Client --> PrototypeRegistry

Ejemplo práctico

classDiagram
    namespace GameExample {
        class GameCharacter {
            <<interface>>
            +clone() GameCharacter
            +customize(name, level)
        }
        
        class Warrior {
            -skills: List~String~
            -equipment: Equipment
            -stats: Stats
            +clone() Warrior
            +customize(name, level)
        }
        
        class Mage {
            -spells: List~String~
            -manaPool: int
            -stats: Stats
            +clone() Mage
            +customize(name, level)
        }
        
        class CharacterRegistry {
            -prototypes: Map~String, GameCharacter~
            +registerPrototype(type, character)
            +createCharacter(type, name, level) GameCharacter
        }
        
        class GameSession {
            -registry: CharacterRegistry
            +createPlayerCharacter(type, name) GameCharacter
        }
    }
    
    GameCharacter <|.. Warrior
    GameCharacter <|.. Mage
    CharacterRegistry --> GameCharacter
    GameSession --> CharacterRegistry

Tipos de clonación

Shallow Clone: Copia el objeto pero comparte referencias internas (arrays, objetos) Deep Clone: Copia el objeto y todos sus objetos internos independientemente

graph LR
    subgraph "Shallow Clone"
        O1[Original] --> A1["name: 'Warrior'"]
        O1 --> B1["skills: ['Attack']"]
        C1[Clone] --> A2["name: 'Warrior'"]
        C1 --> B1
        style B1 fill:#ffcccc
    end
    
    subgraph "Deep Clone"
        O2[Original] --> A3["name: 'Warrior'"]
        O2 --> B2["skills: ['Attack']"]
        C2[Clone] --> A4["name: 'Warrior'"]
        C2 --> B3["skills: ['Attack']"]
        style B2 fill:#ccffcc
        style B3 fill:#ccffcc
    end

Flujo de clonación

sequenceDiagram
    participant Client
    participant Registry
    participant Prototype
    participant Clone
    
    Client->>Registry: createCharacter("warrior", "Aragorn")
    Registry->>Registry: getPrototype("warrior")
    Registry->>Prototype: clone()
    Prototype->>Clone: deepCopy(this)
    Clone-->>Registry: new character
    Registry->>Clone: customize("Aragorn", 1)
    Registry-->>Client: GameCharacter

Ventajas

  • Performance: Evita el costo de creación de objetos complejos
  • Flexibilidad: Permite crear objetos en tiempo de ejecución
  • Configuración: Útil para objetos con muchas configuraciones predefinidas
  • Independencia: Reduce dependencias de clases concretas

Desventajas

  • Complejidad: Implementar deep cloning puede ser complejo
  • Referencias circulares: Problemas con objetos que se referencian mutuamente
  • Mantenimiento: Cambios en la clase requieren actualizar el método clone
  • Inmutabilidad: Los prototipos deben manejar correctamente la mutabilidad

Cuándo usar

  • La creación de objetos es costosa (consultas DB, cálculos complejos)
  • Necesitas muchas variaciones de un objeto base
  • Quieres evitar subclases de Factory
  • Los objetos tienen configuraciones complejas que se reutilizan

Cuándo NO usar

  • La creación de objetos es simple y rápida
  • Los objetos no comparten configuraciones comunes
  • Deep cloning es demasiado complejo de implementar
  • Prefieres inmutabilidad sobre clonación

Diferencias con otros patrones

  • vs Factory Method: Prototype clona existentes, Factory crea desde cero
  • vs Builder: Prototype clona configurados, Builder construye paso a paso