Patrón Prototype
Crea objetos nuevos copiando uno ya configurado (el prototipo) en lugar de construirlos desde cero: plantillas de documentos, enemigos de un juego. La clave es que la copia sea profunda.
nivel intermedioTambién: prototipo, clonar, clone, copia profunda
Visualízalo paso a paso
Cambia los datos, dale a reproducir y sigue cada paso en el dibujo, en la línea de Java que se ejecuta y en sus variables.
Prototype
Elige cómo se copia la plantilla del acta y qué secciones se añaden a la copia, separadas por comas.
Paso 1
La plantilla del acta ya está configurada. Para un acta nueva no se empieza de cero: se le pide una copia.
1Documento acta = plantilla.copia(); // copia = profunda, listas = 1
2acta.secciones.add("Ruegos y preguntas");
3
4Documento copia() {
5 return new Documento(titulo, secciones);
6}
7
8Documento(String titulo, List<String> secciones) {
9 this.titulo = titulo;
10 this.secciones = new ArrayList<>(secciones);
11}
12
13Documento copiaSuperficial() {
14 Documento d = new Documento(titulo, List.of());
15 d.secciones = secciones;
16 return d;
17}Variables
- copia
- profunda
- listas
- 1
Atajos con el foco dentro del visualizador: ← → paso a paso, Espacio reproducir o pausar, Inicio/Fin ir al principio o al final.
La idea
Hay objetos que cuesta mucho configurar (una plantilla de documento con sus secciones, un enemigo con sus estadísticas y su inventario, un formulario con sus campos) y de los que se necesitan muchos parecidos. En lugar de repetir toda la configuración, se guarda uno ya preparado, el prototipo, y cada objeto nuevo es una copia suya que después se retoca.
El patrón pide que el propio objeto sepa copiarse (un método copia() o clone()), porque es el único que conoce todos sus campos, también los privados. A menudo se combina con un registro de prototipos: un Map de nombre a prototipo, y crear un objeto es pedir una copia del que toca.
La trampa está en la copia superficial: copiar un objeto que tiene una lista copia la referencia a la lista, no la lista. La copia y el original comparten entonces sus secciones, y cambiar una cambia la otra. Una copia profunda crea también copias de los objetos mutables que contiene, hasta el nivel que haga falta.
Cuándo usarlo
- Crear un objeto desde cero es caro o largo (muchos pasos de configuración, datos leídos de un fichero o de la red) y hacen falta muchos parecidos.
- Los objetos se diferencian en pocos valores de una configuración común (plantillas, enemigos de un tipo, piezas de un editor).
- El programa debe crear objetos de clases que no conoce, a partir de ejemplos que le dan (un editor que duplica la figura seleccionada, sea cual sea).
Cuándo no
- Si crear el objeto es barato y sencillo: un constructor es más claro.
- Si el objeto tiene referencias circulares o recursos que no se pueden duplicar (conexiones abiertas, ficheros): copiar bien es difícil y propenso a errores.
Participantes
- Prototipo. La clase con el método de copia (
copia()oclone()), que crea un objeto nuevo con los mismos valores y copias propias de sus partes mutables. - Registro de prototipos. Opcional: un
Mapde nombre a prototipo ya configurado (plantillas). - Cliente. Pide una copia del prototipo y la retoca (cambia el título, añade una sección) sin tocar el original.
Diagrama de clases
Ver el diagrama en PlantUML
1@startuml
2class Documento {
3 -titulo : String
4 -secciones : List<String>
5 +Documento(titulo : String, secciones : List<String>)
6 +copia() : Documento
7}
8class Plantillas {
9 -prototipos : Map<String, Documento>
10 +registrar(nombre : String, d : Documento) : void
11 +crear(nombre : String) : Documento
12}
13class Cliente
14Plantillas o-- "0..*" Documento : prototipos
15Cliente ..> Plantillas : crear()
16@endumlPuedes copiarlo en el editor de diagramas UML y modificarlo.
El código
Plantillas de documentos
La copia profunda deja la plantilla intacta; la superficial comparte la lista y la estropea.
1import java.util.*;
2
3/** El prototipo: un documento ya configurado que sabe hacer una copia de sí mismo. */
4class Documento {
5 String titulo;
6 List<String> secciones;
7
8 Documento(String titulo, List<String> secciones) {
9 this.titulo = titulo;
10 this.secciones = new ArrayList<>(secciones);
11 }
12
13 /** Copia profunda: la copia tiene su propia lista de secciones. */
14 Documento copia() {
15 return new Documento(titulo, secciones); // el constructor copia la lista
16 }
17
18 /** Copia superficial (el error típico): la copia comparte la lista con el original. */
19 Documento copiaSuperficial() {
20 Documento d = new Documento(titulo, List.of());
21 d.secciones = secciones;
22 return d;
23 }
24
25 @Override
26 public String toString() { return titulo + " " + secciones; }
27}
28
29public class Main {
30 public static void main(String[] args) {
31 // Un registro de prototipos: crear un documento es copiar una plantilla ya configurada
32 Map<String, Documento> plantillas = new LinkedHashMap<>();
33 plantillas.put("acta", new Documento("Acta de reunión", List.of("Asistentes", "Orden del día", "Acuerdos")));
34 plantillas.put("informe", new Documento("Informe técnico", List.of("Resumen", "Análisis", "Conclusiones")));
35
36 Documento acta = plantillas.get("acta").copia();
37 acta.titulo = "Acta del 3 de octubre";
38 acta.secciones.add("Ruegos y preguntas");
39 System.out.println("Copia: " + acta);
40 System.out.println("Plantilla: " + plantillas.get("acta"));
41
42 Documento informe = plantillas.get("informe").copiaSuperficial();
43 informe.secciones.add("Anexo");
44 System.out.println("Plantilla tras una copia superficial: " + plantillas.get("informe"));
45 }
46}class Documento:
"""El prototipo: un documento ya configurado que sabe hacer una copia de sí mismo."""
def __init__(self, titulo, secciones):
self.titulo = titulo
self.secciones = list(secciones)
def copia(self):
"""Copia profunda: la copia tiene su propia lista de secciones."""
return Documento(self.titulo, self.secciones) # el constructor copia la lista
def copia_superficial(self):
"""Copia superficial (el error típico): la copia comparte la lista con el original."""
d = Documento(self.titulo, [])
d.secciones = self.secciones
return d
def __str__(self):
return f"{self.titulo} [{', '.join(self.secciones)}]"
# Un registro de prototipos: crear un documento es copiar una plantilla ya configurada
plantillas = {
"acta": Documento("Acta de reunión", ["Asistentes", "Orden del día", "Acuerdos"]),
"informe": Documento("Informe técnico", ["Resumen", "Análisis", "Conclusiones"]),
}
acta = plantillas["acta"].copia()
acta.titulo = "Acta del 3 de octubre"
acta.secciones.append("Ruegos y preguntas")
print(f"Copia: {acta}")
print(f"Plantilla: {plantillas['acta']}")
informe = plantillas["informe"].copia_superficial()
informe.secciones.append("Anexo")
print(f"Plantilla tras una copia superficial: {plantillas['informe']}")/** El prototipo: un documento ya configurado que sabe hacer una copia de sí mismo. */
class Documento {
constructor(titulo, secciones) {
this.titulo = titulo;
this.secciones = [...secciones];
}
/** Copia profunda: la copia tiene su propia lista de secciones. */
copia() {
return new Documento(this.titulo, this.secciones); // el constructor copia la lista
}
/** Copia superficial (el error típico): la copia comparte la lista con el original. */
copiaSuperficial() {
const d = new Documento(this.titulo, []);
d.secciones = this.secciones;
return d;
}
toString() { return `${this.titulo} [${this.secciones.join(", ")}]`; }
}
// Un registro de prototipos: crear un documento es copiar una plantilla ya configurada
const plantillas = new Map([
["acta", new Documento("Acta de reunión", ["Asistentes", "Orden del día", "Acuerdos"])],
["informe", new Documento("Informe técnico", ["Resumen", "Análisis", "Conclusiones"])],
]);
const acta = plantillas.get("acta").copia();
acta.titulo = "Acta del 3 de octubre";
acta.secciones.push("Ruegos y preguntas");
console.log(`Copia: ${acta}`);
console.log(`Plantilla: ${plantillas.get("acta")}`);
const informe = plantillas.get("informe").copiaSuperficial();
informe.secciones.push("Anexo");
console.log(`Plantilla tras una copia superficial: ${plantillas.get("informe")}`);using System;
using System.Collections.Generic;
// El prototipo: un documento ya configurado que sabe hacer una copia de sí mismo.
class Documento {
public string Titulo;
public List<string> Secciones;
public Documento(string titulo, IEnumerable<string> secciones) {
Titulo = titulo;
Secciones = new List<string>(secciones);
}
// Copia profunda: la copia tiene su propia lista de secciones.
public Documento Copia() => new(Titulo, Secciones); // el constructor copia la lista
// Copia superficial (el error típico): la copia comparte la lista con el original.
public Documento CopiaSuperficial() => (Documento)MemberwiseClone();
public override string ToString() => quot;{Titulo} [{string.Join(", ", Secciones)}]";
}
class Program {
static void Main() {
// Un registro de prototipos: crear un documento es copiar una plantilla ya configurada
var plantillas = new Dictionary<string, Documento> {
["acta"] = new("Acta de reunión", new[] { "Asistentes", "Orden del día", "Acuerdos" }),
["informe"] = new("Informe técnico", new[] { "Resumen", "Análisis", "Conclusiones" }),
};
Documento acta = plantillas["acta"].Copia();
acta.Titulo = "Acta del 3 de octubre";
acta.Secciones.Add("Ruegos y preguntas");
Console.WriteLine(quot;Copia: {acta}");
Console.WriteLine(quot;Plantilla: {plantillas["acta"]}");
Documento informe = plantillas["informe"].CopiaSuperficial();
informe.Secciones.Add("Anexo");
Console.WriteLine(quot;Plantilla tras una copia superficial: {plantillas["informe"]}");
}
}<?php
/**
* El prototipo: un documento ya configurado que sabe hacer una copia de sí mismo.
* Las secciones van en un ArrayObject porque los arrays de PHP se copian al asignarlos;
* los objetos, en cambio, se comparten, como en Java.
*/
class Documento {
public ArrayObject $secciones;
public function __construct(public string $titulo, array $secciones) {
$this->secciones = new ArrayObject($secciones);
}
/** Copia profunda: la copia tiene su propia lista de secciones. */
public function copia(): Documento {
return new Documento($this->titulo, $this->secciones->getArrayCopy());
}
/** Copia superficial (el error típico): clone copia campo a campo y comparte la lista. */
public function copiaSuperficial(): Documento {
return clone $this;
}
public function __toString(): string {
return $this->titulo . " [" . implode(", ", $this->secciones->getArrayCopy()) . "]";
}
}
// Un registro de prototipos: crear un documento es copiar una plantilla ya configurada
$plantillas = [
"acta" => new Documento("Acta de reunión", ["Asistentes", "Orden del día", "Acuerdos"]),
"informe" => new Documento("Informe técnico", ["Resumen", "Análisis", "Conclusiones"]),
];
$acta = $plantillas["acta"]->copia();
$acta->titulo = "Acta del 3 de octubre";
$acta->secciones->append("Ruegos y preguntas");
echo "Copia: $acta\n";
echo "Plantilla: {$plantillas["acta"]}\n";
$informe = $plantillas["informe"]->copiaSuperficial();
$informe->secciones->append("Anexo");
echo "Plantilla tras una copia superficial: {$plantillas["informe"]}\n";Salida al ejecutarlo (la misma en los 5 lenguajes)
Copia: Acta del 3 de octubre [Asistentes, Orden del día, Acuerdos, Ruegos y preguntas] Plantilla: Acta de reunión [Asistentes, Orden del día, Acuerdos] Plantilla tras una copia superficial: Informe técnico [Resumen, Análisis, Conclusiones, Anexo]
clone() y el constructor de copia
Cómo se copia en cada lenguaje, y la diferencia entre la copia superficial que da el lenguaje y la profunda que hay que escribir.
1/** Con clone(): Object.clone copia campo a campo, y los objetos mutables se copian a mano. */
2class Documento implements Cloneable {
3 String titulo;
4 ArrayList<String> secciones = new ArrayList<>();
5
6 @Override
7 public Documento clone() {
8 try {
9 Documento copia = (Documento) super.clone(); // copia superficial de todos los campos
10 copia.secciones = new ArrayList<>(secciones); // y una lista nueva para la copia
11 return copia;
12 } catch (CloneNotSupportedException e) {
13 throw new AssertionError(e); // no pasa: la clase implementa Cloneable
14 }
15 }
16}
17
18// Effective Java recomienda un constructor de copia o un método copia() en lugar de clone():
19// no hace falta Cloneable, no hay excepciones ni casts y funciona con campos final.
20Documento copia = new Documento(original);import copy
original = Documento("Informe técnico", ["Resumen", "Análisis"])
superficial = copy.copy(original) # un objeto nuevo... que comparte la lista
profunda = copy.deepcopy(original) # copia también todo lo que contiene, a cualquier profundidad
# Una clase puede decidir cómo se copia definiendo __copy__ y __deepcopy__.const original = { titulo: "Informe técnico", secciones: ["Resumen", "Análisis"] };
const superficial = { ...original }; // un objeto nuevo... que comparte la lista
const profunda = structuredClone(original); // copia todo (pero no funciones ni clases)
// En JavaScript los objetos heredan de prototipos de verdad:
const plantilla = { idioma: "es", saludar() { return "Hola"; } };
const doc = Object.create(plantilla); // doc no copia nada: busca en plantilla lo que no tieneclass Documento : ICloneable {
public string Titulo = "";
public List<string> Secciones = new();
// MemberwiseClone copia campo a campo (superficial); la lista se copia a mano
public Documento Copia() {
var copia = (Documento)MemberwiseClone();
copia.Secciones = new List<string>(Secciones);
return copia;
}
object ICloneable.Clone() => Copia();
}
// Los record traen una copia con cambios (también superficial):
record Plantilla(string Titulo, int Version);
var nueva = plantilla with { Version = 2 };<?php
class Documento {
public function __construct(public string $titulo, public ArrayObject $secciones) {}
// clone copia el objeto campo a campo (superficial); __clone se ejecuta sobre la copia
// para duplicar a mano lo que no se debe compartir.
public function __clone() {
$this->secciones = new ArrayObject($this->secciones->getArrayCopy());
}
}
$copia = clone $original;
// Ojo: los arrays de PHP se copian al asignarlos (nunca se comparten); los objetos no.En la práctica
clone()de los arrays y deArrayListen Java (ojo: superficial),copy.deepcopyen Python ystructuredCloneen JavaScript.- Los editores (de diagramas, de niveles de un juego, de presentaciones): «duplicar» copia el elemento seleccionado sea cual sea su clase.
- Los motores de juegos instancian enemigos y objetos a partir de prefabs: Unity llama
Instantiate(prefab)a copiar un prototipo. - En JavaScript todo el sistema de objetos se basa en prototipos:
Object.create(proto)crea un objeto que hereda de otro.
Errores típicos
- Hacer una copia superficial de un objeto con listas o mapas: la copia y el original comparten los datos y los cambios se cuelan de uno a otro.
- Copiar la lista pero no los objetos mutables de dentro: la lista es nueva, pero sus elementos siguen siendo los mismos (la copia tiene que bajar todos los niveles mutables).
- Devolver
thisencopia(): no hay copia, solo otra referencia al mismo objeto. - Usar
clone()sin implementarCloneable(lanzaCloneNotSupportedException) o olvidar queObject.clonees superficial.
Ejercicios
Cada ejercicio se corrige solo con sus pruebas (algunas ocultas). Escribe tu solución en el editor y pulsa Ejecutar o Comprobar; la solución explicada está debajo, por si te atascas.
1. Enemigos a partir de prototipos
Cada enemigo nuevo se crea copiando el prototipo de su tipo y poniéndole nombre. Pero copia() devuelve el propio prototipo: al crear dos orcos, los dos son el mismo objeto, y lo que le pasa a uno le pasa al otro (y al prototipo). Escribe copia() para que cree un enemigo independiente, con su propia lista de objetos.
- Órdenes:
crear tipo nombre(orco,arquerootroll),dar nombre objeto,golpear nombre daño,ver nombreyprototipo tipo. Al final,Quedan N enemigos en pie. - Los mensajes ya están escritos: solo falta que cada copia sea independiente del prototipo y de las demás copias.
Ejemplo
crear orco Grishnak crear orco Ugluk dar Grishnak pocion golpear Ugluk 12 ver Grishnak ver Ugluk prototipo orco
Creado: Grishnak (orco) Creado: Ugluk (orco) Grishnak recibe pocion Ugluk: vida 18 Grishnak (orco): vida 30, ataque 5, objetos [hacha, pocion] Ugluk (orco): vida 18, ataque 5, objetos [hacha] Prototipo orco (orco): vida 30, ataque 5, objetos [hacha] Quedan 2 enemigos en pie
Ver la solución explicada
1import java.util.*;
2
3/** Un enemigo del juego. Los nuevos se crean copiando un prototipo ya configurado. */
4class Enemigo {
5 final String tipo;
6 String nombre;
7 int vida;
8 final int ataque;
9 List<String> objetos;
10
11 Enemigo(String tipo, int vida, int ataque, List<String> objetos) {
12 this.tipo = tipo;
13 this.nombre = tipo;
14 this.vida = vida;
15 this.ataque = ataque;
16 this.objetos = new ArrayList<>(objetos);
17 }
18
19 /** Una copia independiente: cambiar la copia no cambia el prototipo ni las demás copias. */
20 Enemigo copia() {
21 Enemigo e = new Enemigo(tipo, vida, ataque, objetos); // el constructor copia la lista
22 e.nombre = nombre;
23 return e;
24 }
25
26 String ficha() {
27 return nombre + " (" + tipo + "): vida " + vida + ", ataque " + ataque + ", objetos " + objetos;
28 }
29}
30
31public class Main {
32 public static void main(String[] args) {
33 Map<String, Enemigo> prototipos = new HashMap<>();
34 prototipos.put("orco", new Enemigo("orco", 30, 5, List.of("hacha")));
35 prototipos.put("arquero", new Enemigo("arquero", 20, 7, List.of("arco", "flechas")));
36 prototipos.put("troll", new Enemigo("troll", 80, 12, List.of("garrote")));
37
38 Map<String, Enemigo> enemigos = new LinkedHashMap<>();
39 Scanner sc = new Scanner(System.in);
40 while (sc.hasNextLine()) {
41 String[] p = sc.nextLine().trim().split("\\s+");
42 if (p[0].isEmpty()) continue;
43 String orden = p[0] + "/" + (p.length - 1);
44 Enemigo e = p.length > 1 && !p[0].equals("crear") && !p[0].equals("prototipo") ? enemigos.get(p[1]) : null;
45 if (e == null && p.length > 1 && (p[0].equals("dar") || p[0].equals("golpear") || p[0].equals("ver"))) {
46 System.out.println("No existe: " + p[1]);
47 continue;
48 }
49 switch (orden) {
50 case "crear/2" -> {
51 Enemigo proto = prototipos.get(p[1]);
52 if (proto == null) System.out.println("Tipo desconocido: " + p[1]);
53 else if (enemigos.containsKey(p[2])) System.out.println("Ya existe: " + p[2]);
54 else {
55 Enemigo nuevo = proto.copia();
56 nuevo.nombre = p[2];
57 enemigos.put(p[2], nuevo);
58 System.out.println("Creado: " + p[2] + " (" + p[1] + ")");
59 }
60 }
61 case "dar/2" -> {
62 e.objetos.add(p[2]);
63 System.out.println(e.nombre + " recibe " + p[2]);
64 }
65 case "golpear/2" -> {
66 try {
67 e.vida = Math.max(0, e.vida - Integer.parseInt(p[2]));
68 System.out.println(e.vida == 0 ? e.nombre + " ha caído" : e.nombre + ": vida " + e.vida);
69 } catch (NumberFormatException ex) {
70 System.out.println("Orden no válida: " + String.join(" ", p));
71 }
72 }
73 case "ver/1" -> System.out.println(e.ficha());
74 case "prototipo/1" -> {
75 Enemigo proto = prototipos.get(p[1]);
76 System.out.println(proto == null ? "Tipo desconocido: " + p[1] : "Prototipo " + proto.ficha());
77 }
78 default -> System.out.println("Orden no válida: " + String.join(" ", p));
79 }
80 }
81 System.out.println("Quedan " + enemigos.values().stream().filter(x -> x.vida > 0).count() + " enemigos en pie");
82 }
83}return this no copia nada: devuelve otra referencia al mismo objeto, y todos los «orcos» eran el prototipo con distintos nombres sucesivos.
La copia profunda crea una lista nueva: dar un objeto a un enemigo ya no se lo da a todos los de su tipo.
2. Formularios a partir de plantillas
Los formularios se crean copiando una plantilla (nuevo) o un formulario que ya existe (duplicar), y luego se rellenan. La copia actual crea una lista nueva pero mete en ella los mismos objetos Campo, así que rellenar un formulario rellena también la plantilla y todas sus copias. Haz que la copia sea profunda de verdad: añade copia() a Campo y úsalo en Formulario.copia().
- Órdenes:
nuevo plantilla nombre,duplicar formulario nombre,rellenar nombre campo valor(el valor puede tener espacios),obligatorio nombre campo,ver nombreyenviar nombre. verescribenombre (plantilla): campo = valor · ..., con*tras los obligatorios y(vacío)si no tienen valor.enviarescribeEnviado: nombreoFalta: campos obligatorios vacíos.
Ejemplo
nuevo matricula f1 rellenar f1 nombre Ana López rellenar f1 telefono 600 111 222 enviar f1 ver f1 ver matricula rellenar f1 dni 12345678Z enviar f1
Creado: f1 (copia de matricula) Falta: dni f1 (matricula): nombre* = Ana López · dni* = (vacío) · telefono = 600 111 222 · ciclo* = DAM matricula (matricula): nombre* = (vacío) · dni* = (vacío) · telefono = (vacío) · ciclo* = DAM Enviado: f1
Ver la solución explicada
1import java.util.*;
2
3/** Un campo de un formulario: mutable (se rellena y se puede volver obligatorio). */
4class Campo {
5 final String nombre;
6 boolean obligatorio;
7 String valor;
8
9 Campo(String nombre, boolean obligatorio, String valor) {
10 this.nombre = nombre;
11 this.obligatorio = obligatorio;
12 this.valor = valor;
13 }
14
15 Campo copia() { return new Campo(nombre, obligatorio, valor); }
16
17 @Override
18 public String toString() { return nombre + (obligatorio ? "*" : "") + " = " + (valor == null ? "(vacío)" : valor); }
19}
20
21class Formulario {
22 final String plantilla;
23 final List<Campo> campos;
24
25 Formulario(String plantilla, List<Campo> campos) {
26 this.plantilla = plantilla;
27 this.campos = campos;
28 }
29
30 /** Copia profunda de dos niveles: una lista nueva y, dentro, una copia de cada campo. */
31 Formulario copia() {
32 List<Campo> copias = new ArrayList<>();
33 for (Campo c : campos) copias.add(c.copia());
34 return new Formulario(plantilla, copias);
35 }
36
37 Campo campo(String nombre) {
38 for (Campo c : campos) if (c.nombre.equals(nombre)) return c;
39 return null;
40 }
41}
42
43public class Main {
44 public static void main(String[] args) {
45 Map<String, Formulario> formularios = new LinkedHashMap<>();
46 formularios.put("matricula", new Formulario("matricula", List.of(
47 new Campo("nombre", true, null), new Campo("dni", true, null),
48 new Campo("telefono", false, null), new Campo("ciclo", true, "DAM"))));
49 formularios.put("incidencia", new Formulario("incidencia", List.of(
50 new Campo("aula", true, null), new Campo("descripcion", true, null), new Campo("urgente", false, "no"))));
51 Set<String> plantillas = Set.of("matricula", "incidencia");
52
53 Scanner sc = new Scanner(System.in);
54 while (sc.hasNextLine()) {
55 String linea = sc.nextLine().trim();
56 if (linea.isEmpty()) continue;
57 String[] p = linea.split("\\s+", 4);
58 Formulario f = p.length > 1 ? formularios.get(p[1]) : null;
59 switch (p[0] + "/" + Math.min(p.length - 1, 3)) {
60 case "nuevo/2", "duplicar/2" -> {
61 if (f == null) System.out.println("No existe: " + p[1]);
62 else if (formularios.containsKey(p[2])) System.out.println("Ya existe: " + p[2]);
63 else if (p[0].equals("nuevo") && !plantillas.contains(p[1])) System.out.println("No es una plantilla: " + p[1]);
64 else {
65 formularios.put(p[2], f.copia());
66 System.out.println("Creado: " + p[2] + " (copia de " + p[1] + ")");
67 }
68 }
69 case "rellenar/3", "obligatorio/2" -> {
70 Campo c = f == null ? null : f.campo(p[2]);
71 if (f == null) System.out.println("No existe: " + p[1]);
72 else if (plantillas.contains(p[1])) System.out.println("Las plantillas no se tocan: " + p[1]);
73 else if (c == null) System.out.println("Campo desconocido: " + p[2]);
74 else if (p[0].equals("rellenar")) c.valor = p[3];
75 else c.obligatorio = true;
76 }
77 case "ver/1" -> {
78 if (f == null) System.out.println("No existe: " + p[1]);
79 else {
80 List<String> partes = new ArrayList<>();
81 for (Campo c : f.campos) partes.add(c.toString());
82 System.out.println(p[1] + " (" + f.plantilla + "): " + String.join(" · ", partes));
83 }
84 }
85 case "enviar/1" -> {
86 if (f == null) System.out.println("No existe: " + p[1]);
87 else {
88 List<String> faltan = new ArrayList<>();
89 for (Campo c : f.campos) if (c.obligatorio && c.valor == null) faltan.add(c.nombre);
90 System.out.println(faltan.isEmpty() ? "Enviado: " + p[1] : "Falta: " + String.join(", ", faltan));
91 }
92 }
93 default -> System.out.println("Orden no válida: " + linea);
94 }
95 }
96 }
97}Una copia profunda baja por todos los niveles mutables: la lista y cada campo. Los String son inmutables, así que pueden compartirse sin peligro.
duplicar usa el mismo copia(): el prototipo puede ser cualquier objeto, no solo una plantilla.
Test
Test: Prototype
0/5 respondidas · 0 aciertosElige una respuesta en cada pregunta: verás al momento si es correcta y por qué. Con un 80 % de aciertos se da por superada.
1.¿Qué hace el patrón Prototype para crear un objeto?
2.Un objeto tiene una lista. Con una copia superficial, ¿qué pasa al añadir un elemento a la lista de la copia?
3.¿Qué tipo de copia hace Object.clone() en Java?
4.Para copiar un formulario con una lista de campos mutables, ¿qué hace falta?
5.¿Qué se puede compartir sin copiar en una copia profunda?