Ao criar um app, às vezes é preciso gerar milhares ou dezenas de milhares de objetos semelhantes. Um exemplo típico é colocar dezenas de milhares de marcadores em um mapa; nessas situações, o gráfico de memória pode continuar subindo facilmente.
É aí que o padrão Flyweight pode ajudar. Em resumo, ele combina e compartilha os dados usados por vários objetos para reduzir bastante o uso de memória. Em vez de criar a mesma coisa repetidamente, você cria uma vez e reutiliza.
Neste artigo, vamos ver o que é o padrão Flyweight no Swift, quando usá-lo e como implementá-lo com código real.
O que é exatamente o padrão Flyweight?
Flyweight significa “objeto leve”. Também é o nome da categoria mais leve do boxe.
A ideia é simples: dividir os dados de um objeto em dois tipos.
- Estado intrínseco: dados imutáveis que vários objetos podem compartilhar, como a espécie, a textura ou a cor de uma árvore.
- Estado extrínseco: dados que variam entre os objetos e mudam conforme a situação, como a posição ou o tamanho de uma árvore.
Imagine que estamos desenhando uma floresta. E se precisássemos desenhar 1 milhão de árvores, mas elas fossem apenas de três espécies: pinheiro, carvalho e bordo?
Basta criar e compartilhar três conjuntos de informações de textura e cor (estado intrínseco) e manter separados 1 milhão de valores de posição (estado extrínseco).
Criar e compartilhar uma única vez os dados comuns pesados e gerenciar separadamente apenas os dados individuais leves. Isso é todo o padrão Flyweight.
Vamos implementar com código Swift
Só com palavras é difícil visualizar, então vamos direto ao código. Vamos transportar exatamente o exemplo das árvores.
Primeiro, o objeto Flyweight que armazena o estado intrínseco a ser compartilhado.
// Dados pesados compartilhados (estado intrínseco)
final class TreeType {
let name: String
let color: String
let texture: Data // Textura grande
init(name: String, color: String, texture: Data) {
self.name = name
self.color = color
self.texture = texture
}
}
Em seguida, criamos uma fábrica que gerencia e compartilha esses TreeType. Se um tipo já foi criado, ele é reutilizado em vez de ser criado novamente.
// Reutilizar o mesmo tipo sem criá-lo novamente
final class TreeFactory {
private var pool: [String: TreeType] = [:]
func treeType(name: String, color: String, texture: Data) -> TreeType {
let key = "\(name)-\(color)"
if let existing = pool[key] { return existing }
let type = TreeType(name: name, color: color, texture: texture)
pool[key] = type
return type
}
}
Agora, a árvore real armazena apenas o estado extrínseco, como a posição, e apenas referencia os dados pesados por meio de TreeType.
// Cada árvore armazena apenas a posição e compartilha o tipo por referência
struct Tree {
let x: Double
let y: Double
let type: TreeType // Referência ao objeto compartilhado
}
Mesmo plantando 1 milhão de árvores, haverá apenas tantas instâncias de TreeType quanto espécies — por exemplo, três. Todo o restante são apenas referências a essas três.
Quando usar e quando não usar?
O padrão Flyweight nem sempre é a resposta certa. Em muitos casos, é melhor não usá-lo. Veja o resumo por situação.
| Situação | Flyweight |
|---|---|
| Criar muitos objetos com a mesma natureza | Adequado |
| Os objetos têm dados pesados que podem ser compartilhados | Adequado |
| Poucos objetos | Desnecessário (apenas aumenta a complexidade) |
| A maior parte do estado é diferente em cada objeto | Benefício mínimo |
O padrão Flyweight se encaixa quando estas duas condições coincidem: muitos objetos e dados pesados que podem ser compartilhados.
No Swift, class já é compartilhada por referência por ser um tipo por referência. Por isso, o padrão é especialmente eficaz quando um struct, que é um tipo por valor, contém dados pesados e é copiado repetidamente.
Perguntas frequentes
P. Qual é a diferença em relação ao padrão Singleton?
Singleton força uma classe a ter exatamente uma instância. Flyweight permite várias instâncias, mas compartilha apenas o que pode ser compartilhado — por exemplo, três tipos de árvore e 1 milhão de árvores.
P. Eu confundo com cache.
O objetivo é diferente. O cache armazena resultados para evitar recalculá-los ou consultá-los novamente, enquanto o Flyweight gerencia objetos compartilhados para economizar memória. Ainda assim, o pool da fábrica funciona de modo parecido com um cache.
P. Ele também é usado na Swift Standard Library?
Sim. Internação de strings e cache de inteiros pequenos são otimizações baseadas em uma ideia semelhante: compartilhar valores idênticos em vez de criá-los sempre.
O nome padrão Flyweight pode parecer complicado, mas a ideia é bem simples: “Crie uma vez o que é pesado e se repete, e compartilhe.”
Se você precisa lidar com muitos objetos semelhantes, como marcadores de mapa, partículas, tile maps ou renderização de texto, vale lembrar dele. Você verá o gráfico de memória ficar visivelmente mais estável.

