Pular para o conteúdo
GoF Padrões de Projeto Estruturais
Estrutural escopo de objeto
GoF, p. 195 também chamado de Cache

Peso-Mosca

Flyweight

Compartilhe a parte imutável entre milhões de objetos e economize memória.

Complexidade alta Frequência de uso rara
Intençãocomo o livro define

Usar compartilhamento para suportar eficientemente grandes quantidades de objetos de granularidade fina.

Analogiauma imagem do mundo real

A floresta do videogame

Um jogo desenha 500 000 árvores. Cada árvore tem posição própria — mas a textura, a malha 3D e a cor são idênticas para todas as árvores da mesma espécie. Guardar a textura 500 000 vezes estoura a memória. Guarda-se uma vez por espécie, e cada árvore no mapa carrega só x, y e um ponteiro para a espécie.

O problema

Você precisa de um número enorme de objetos semelhantes e a memória não aguenta. A maior parte do que cada objeto guarda é idêntica entre eles.

A solução

Separe o estado em duas partes: o **intrínseco** (imutável, compartilhável — a textura) e o **extrínseco** (varia por instância — a posição). Guarde só o intrínseco no flyweight, compartilhado via uma fábrica com cache; passe o extrínseco como parâmetro dos métodos.

Sintomascomo reconhecer no seu código
  • Milhares de objetos idênticos campo a campo no heap dump.
  • Strings ou buffers duplicados aos milhares.

Estrutura

cria muitascompartilha 1cria/reusaClientArvore- x, y- tipo →FabricaDeTipos- pool: Map+ obter(nome)TipoDeArvore- nome, cor, textura+ desenhar(x, y)
Fig. 11 — Flyweight herda / implementa cria usa / contém
Ver este diagrama sendo desenhado, traço a traço
Participantes
Flyweight
Guarda o estado intrínseco compartilhado; deve ser imutável.
FlyweightFactory
Mantém o pool e devolve o flyweight existente, criando só se não houver.
Context
Guarda o estado extrínseco e uma referência ao flyweight.
Client
Calcula/armazena o estado extrínseco.

Implementação

Listagem 11 Uma floresta com 1 milhão de árvores e 3 objetos de textura
// ── Flyweight: SÓ o estado intrínseco, imutável ───────
class TipoDeArvore {
  constructor(nome, cor, textura) {
    this.nome = nome; this.cor = cor; this.textura = textura;  // ~2 KB cada
    Object.freeze(this);   // imutável: pode ser compartilhado sem medo
  }
  // O estado EXTRÍNSECO chega por parâmetro — nunca é armazenado aqui.
  desenhar(canvas, x, y) { /* canvas.draw(this.textura, x, y) */ }
}

// ── Fábrica: garante o compartilhamento ───────────────
class FabricaDeTipos {
  static #pool = new Map();
  static obter(nome, cor, textura) {
    const chave = `${nome}|${cor}`;
    if (!this.#pool.has(chave)) this.#pool.set(chave, new TipoDeArvore(nome, cor, textura));
    return this.#pool.get(chave);       // ← mesma instância para todos
  }
  static get total() { return this.#pool.size; }
}

// ── Context: leve, um por árvore no mapa ──────────────
class Arvore {
  constructor(x, y, tipo) { this.x = x; this.y = y; this.tipo = tipo; }  // ~24 bytes
  desenhar(canvas) { this.tipo.desenhar(canvas, this.x, this.y); }
}

class Floresta {
  arvores = [];
  plantar(x, y, nome, cor, textura) {
    this.arvores.push(new Arvore(x, y, FabricaDeTipos.obter(nome, cor, textura)));
  }
}

const floresta = new Floresta();
const especies = [['Carvalho', '#2d5016'], ['Pinheiro', '#1a3d0c'], ['Ipê', '#f2c94c']];
for (let i = 0; i < 1_000_000; i++) {
  const [nome, cor] = especies[i % 3];
  floresta.plantar(i % 1920, (i * 7) % 1080, nome, cor, '<blob de 2KB>');
}

console.log(floresta.arvores.length);   // 1 000 000 contextos leves
console.log(FabricaDeTipos.total);      // 3 flyweights — ~2 GB viraram ~6 KB
Na práticaonde ele já existe
  • java.lang.Integer.valueOf() — cache de -128 a 127
  • String interning na JVM e em outras runtimes
  • Glyphs de fonte em editores de texto; tiles em jogos 2D

Consequências

A favor
  • Redução drástica de memória quando há muitos objetos semelhantes.
  • Menos pressão no coletor de lixo.
Contra
  • Troca memória por CPU: o estado extrínseco precisa ser recalculado ou repassado a cada chamada.
  • O código fica bem mais difícil de ler — a entidade “árvore” virou duas classes.
  • Só compensa em escala; é uma otimização, não um padrão de design de domínio.
Use quando
  • Você tem MUITOS objetos, a memória é o gargalo real, e você já mediu isso.
  • Boa parte do estado dos objetos é duplicada e pode ser extraída como imutável.
Evite quando
  • A quantidade de objetos é modesta — é otimização prematura pura.
  • O estado dos objetos é majoritariamente único.

Relações com outros padrões

costuma andar junto
Composite
Folhas compartilhadas de uma árvore Composite são flyweights naturais.
Abstract Factory
A fábrica de flyweights é uma fábrica com cache.
parecido / fácil de confundir
Singleton
Se o app tiver só um estado intrínseco, o flyweight vira um Singleton — mas a intenção é outra.
Facade
Facade cria um objeto novo para representar um subsistema; Flyweight reutiliza objetos minúsculos existentes.

Verificação

Questãoa resposta está marcada

O que é o estado EXTRÍNSECO de um flyweight?

  1. O estado que varia por instância e é passado por parâmetro, nunca armazenado no flyweight
  2. O estado compartilhado e imutável guardado dentro do flyweight
  3. O cache mantido pela fábrica
  4. O estado persistido em banco de dados

Intrínseco = compartilhado e imutável, mora no flyweight (a textura). Extrínseco = único por uso, mora no contexto ou vem como argumento (a posição x, y).