GoF, p. 117 também chamado de Clone
Protótipo
Prototype
Criar copiando um objeto existente, sem depender da classe dele.
Especificar os tipos de objetos a criar usando uma instância protótipo, e criar novos objetos copiando esse protótipo.
A divisão celular
Uma célula não consulta o “manual de fabricação de células” para gerar outra: ela se divide, e a cópia já nasce com todo o estado interno da original. É mais rápido e não exige saber nada sobre como aquela célula foi montada originalmente.
Você quer copiar um objeto. Copiar campo a campo de fora é impossível quando há campos privados, e mesmo quando é possível, o código passa a depender da classe concreta do objeto — que às vezes você nem conhece (só tem a interface).
Delegue a cópia ao próprio objeto, através de um método `clone()` declarado numa interface comum. O objeto tem acesso aos próprios campos privados e sabe o que copiar em profundidade e o que compartilhar.
- Subclasses cuja única diferença são valores default no construtor.
- Métodos
copiarDe(outro)escritos manualmente fora da classe.
Estrutura
- Prototype
- Interface que declara `clone()`.
- ConcretePrototype
- Implementa a clonagem de si mesmo, inclusive campos privados.
- Client
- Produz novos objetos pedindo `clone()` a um protótipo.
- PrototypeRegistry
- (opcional) Catálogo de protótipos prontos, buscáveis por chave.
Implementação
class Forma {
constructor(origem) {
// Construtor de cópia: aceita outro objeto e absorve seu estado.
if (origem) { this.x = origem.x; this.y = origem.y; this.cor = origem.cor; }
}
clone() { throw new Error('abstrato'); }
}
class Retangulo extends Forma {
constructor(origem) {
super(origem);
if (origem) { this.largura = origem.largura; this.altura = origem.altura; }
}
// Cada classe sabe clonar a si mesma — inclusive campos que ninguém de fora enxerga.
clone() { return new Retangulo(this); }
}
class Circulo extends Forma {
constructor(origem) { super(origem); if (origem) this.raio = origem.raio; }
clone() { return new Circulo(this); }
}
// ── Registro de protótipos: um catálogo pronto ────────
class RegistroDeFormas {
#itens = new Map();
registrar(chave, prototipo) { this.#itens.set(chave, prototipo); }
criar(chave) {
const p = this.#itens.get(chave);
if (!p) throw new Error(`Protótipo "${chave}" não registrado`);
return p.clone(); // ← nunca damos `new` numa classe concreta
}
}
const registro = new RegistroDeFormas();
const botaoPadrao = Object.assign(new Retangulo(), { x: 0, y: 0, cor: '#4f46e5', largura: 120, altura: 40 });
registro.registrar('botao', botaoPadrao);
const b1 = registro.criar('botao');
const b2 = registro.criar('botao');
b2.x = 200; // cópias independentes
console.log(b1.x, b2.x, b1 instanceof Retangulo); // 0 200 true - java.lang.Object.clone() / interface Cloneable
- structuredClone() no JavaScript moderno
- Duplicar um slide, uma camada do Photoshop ou um card do Trello
Consequências
- Clona objetos sem acoplar-se às classes concretas deles.
- Elimina código de inicialização repetido — parta de uma configuração pronta.
- Produz objetos complexos com menos esforço que uma fábrica dedicada.
- Alternativa à herança quando as variações são de configuração, não de comportamento.
- Clonar objetos com referências circulares é complicado.
- A diferença entre cópia rasa e profunda é fonte constante de bugs sutis.
- Seu código não deve depender das classes concretas dos objetos que copia.
- Você quer reduzir subclasses que só diferem no estado inicial.
- Criar o objeto do zero é caro (consulta a banco, parsing, cálculo pesado).
- Os objetos são simples e baratos de criar.
- Há muito estado compartilhado e mutável que torna a cópia arriscada.
Relações com outros padrões
- Abstract Factory
- A fábrica concreta pode ser implementada clonando protótipos.
- Memento
- Memento é uma alternativa ao Prototype para salvar estado (undo).
Verificação
Por que a clonagem é delegada ao próprio objeto, em vez de feita pelo cliente?
- Porque só o objeto acessa seus campos privados e conhece sua classe concreta
- Porque a clonagem precisa ser síncrona
- Porque o cliente não pode usar o operador new
- Porque assim o objeto vira automaticamente um Singleton
Copiar de fora esbarra em encapsulamento e obriga o cliente a conhecer a classe concreta — exatamente o acoplamento que o padrão quer eliminar.