Pular para o conteúdo
GoF Padrões de Projeto Criacionais
Criacional escopo de objeto
GoF, p. 117 também chamado de Clone

Protótipo

Prototype

Criar copiando um objeto existente, sem depender da classe dele.

Complexidade média Frequência de uso rara
Intençãocomo o livro define

Especificar os tipos de objetos a criar usando uma instância protótipo, e criar novos objetos copiando esse protótipo.

Analogiauma imagem do mundo real

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.

O problema

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).

A solução

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.

Sintomascomo reconhecer no seu código
  • Subclasses cuja única diferença são valores default no construtor.
  • Métodos copiarDe(outro) escritos manualmente fora da classe.

Estrutura

clone()catálogoClient«interface»Prototype+ clone()FormaCirculo+ clone()FormaRetangulo+ clone()Registro+ obter(chave)
Fig. 4 — Prototype herda / implementa cria usa / contém
Ver este diagrama sendo desenhado, traço a traço
Participantes
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

Listagem 4 Clonando formas de um editor gráfico
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
Na práticaonde ele já existe
  • java.lang.Object.clone() / interface Cloneable
  • structuredClone() no JavaScript moderno
  • Duplicar um slide, uma camada do Photoshop ou um card do Trello

Consequências

A favor
  • 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.
Contra
  • Clonar objetos com referências circulares é complicado.
  • A diferença entre cópia rasa e profunda é fonte constante de bugs sutis.
Use quando
  • 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).
Evite quando
  • 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

alternativa a
Abstract Factory
A fábrica concreta pode ser implementada clonando protótipos.
Memento
Memento é uma alternativa ao Prototype para salvar estado (undo).
costuma andar junto
Composite
Clonar árvores Composite complexas é um caso de uso clássico.
Decorator
Clonar objetos muito decorados evita remontá-los.

Verificação

Questãoa resposta está marcada

Por que a clonagem é delegada ao próprio objeto, em vez de feita pelo cliente?

  1. Porque só o objeto acessa seus campos privados e conhece sua classe concreta
  2. Porque a clonagem precisa ser síncrona
  3. Porque o cliente não pode usar o operador new
  4. 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.