Pular para o conteúdo
GoF/23 Padrões de Projeto Criacionais
117
Criacional escopo de objeto também: Clone

Prototype

Protótipo

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

Complexidade média Frequência de uso rara
D

Definição

Intenção — palavra por palavra, como o livro define

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

E

Exemplo e motivação

Analogia

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

Diagrama

Client«interface»Prototype+ clone()FormaCirculo+ clone()FormaRetangulo+ clone()Registro+ obter(chave)clone()catálogo
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.
A

Aplicabilidade

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

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.
Apêndice

Código

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
Apêndice

Relaçõ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.
Apêndice

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.