Neste artigo
O Padrão de Projeto Flyweight reduz o consumo de memória compartilhando, entre muitos objetos, a parte dos dados que é igual em todos eles. Cada objeto guarda só o que é dele. O que se repete fica em um objeto único, criado uma vez e reaproveitado.
O nome vem do boxe: flyweight é a categoria peso-mosca, uma das mais leves. A ideia é deixar cada objeto o mais leve possível.

Observe no diagrama os participantes do padrão:
- Flyweight (MarkerStyle): o objeto compartilhado. Guarda os dados que se repetem e recebe o restante por parâmetro.
- FlyweightFactory (MarkerStyleFactory): cria e guarda os flyweights, e devolve sempre a mesma instância para os mesmos dados.
- Contexto (Marker): guarda os dados próprios de cada objeto e a referência para o flyweight.
- Client (DeliveryMap): pede os flyweights à fábrica, em vez de criá-los com
new.
Qual problema o Flyweight resolve?
Pense em um mapa de entregas com milhares de marcadores. Cada marcador tem uma posição (latitude e longitude) e um estilo (ícone e cor). A posição é diferente em cada um, mas o estilo se repete: todos os caminhões são azuis, todas as lojas são verdes.
A solução direta guarda tudo em cada marcador:
<?php
$markers = [];
for ($i = 0; $i < 10000; $i++) {
// Cada marcador cria o seu próprio estilo, mesmo sendo igual aos outros.
$markers[] = new Marker(-23.55, -46.63, new MarkerStyle('truck', 'blue'));
}
São 10.000 marcadores e 10.000 objetos de estilo, embora só exista um estilo diferente. O Flyweight separa os dados do objeto em dois grupos:
| Tipo de estado | No exemplo | Onde fica |
|---|---|---|
| Intrínseco: igual em muitos objetos | Ícone e cor | No MarkerStyle, compartilhado |
| Extrínseco: próprio de cada objeto | Latitude e longitude | No Marker |
Exemplo do padrão Flyweight em PHP
Vamos para o código. O MarkerStyle é o flyweight. Ele guarda o ícone e a cor e recebe a posição por parâmetro na hora de desenhar:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Flyweight;
final class MarkerStyle
{
private readonly string $icon;
private readonly string $color;
public function __construct(string $icon, string $color)
{
$this->icon = $icon;
$this->color = $color;
}
public function render(float $latitude, float $longitude): string
{
return sprintf('%s (%s) at %.4f, %.4f', $this->icon, $this->color, $latitude, $longitude);
}
}
Repare no readonly. Como o mesmo objeto é usado por milhares de marcadores, ele não pode ser alterado depois de criado.
A fábrica é quem garante o compartilhamento. Ela guarda os estilos já criados e só cria um novo quando a combinação de ícone e cor ainda não existe:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Flyweight;
final class MarkerStyleFactory
{
/** @var array<string, MarkerStyle> */
private array $styles = [];
public function get(string $icon, string $color): MarkerStyle
{
$key = $icon . ':' . $color;
if (!isset($this->styles[$key])) {
$this->styles[$key] = new MarkerStyle($icon, $color);
}
return $this->styles[$key];
}
public function count(): int
{
return count($this->styles);
}
}
O Marker guarda a posição e a referência para o estilo compartilhado:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Flyweight;
final class Marker
{
private float $latitude;
private float $longitude;
private MarkerStyle $style;
public function __construct(float $latitude, float $longitude, MarkerStyle $style)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
$this->style = $style;
}
public function render(): string
{
return $this->style->render($this->latitude, $this->longitude);
}
}
E o DeliveryMap é o cliente. Ele sempre pede o estilo à fábrica:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Flyweight;
final class DeliveryMap
{
private MarkerStyleFactory $styles;
/** @var Marker[] */
private array $markers = [];
public function __construct(MarkerStyleFactory $styles)
{
$this->styles = $styles;
}
public function addMarker(float $latitude, float $longitude, string $icon, string $color): void
{
$this->markers[] = new Marker($latitude, $longitude, $this->styles->get($icon, $color));
}
public function count(): int
{
return count($this->markers);
}
/** @return string[] */
public function render(): array
{
return array_map(static fn (Marker $marker): string => $marker->render(), $this->markers);
}
}
Testando o exemplo
Os testes conferem as duas promessas do padrão: o mesmo estilo devolve a mesma instância, e 10.000 marcadores criam só dois objetos de estilo.
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Tests\Structural\Flyweight;
use Growthdev\DesignPatterns\Structural\Flyweight\DeliveryMap;
use Growthdev\DesignPatterns\Structural\Flyweight\MarkerStyleFactory;
use PHPUnit\Framework\TestCase;
final class FlyweightTest extends TestCase
{
public function testExpectSameInstanceForSameStyle(): void
{
$factory = new MarkerStyleFactory();
$this->assertSame($factory->get('truck', 'blue'), $factory->get('truck', 'blue'));
$this->assertSame(1, $factory->count());
}
public function testExpectDifferentInstancesForDifferentStyles(): void
{
$factory = new MarkerStyleFactory();
$this->assertNotSame($factory->get('truck', 'blue'), $factory->get('store', 'green'));
$this->assertSame(2, $factory->count());
}
public function testExpectThousandsOfMarkersToShareFewStyles(): void
{
$factory = new MarkerStyleFactory();
$map = new DeliveryMap($factory);
for ($i = 0; $i < 10000; $i++) {
if ($i % 2 === 0) {
$map->addMarker(-23.55 + $i / 100000, -46.63, 'truck', 'blue');
} else {
$map->addMarker(-23.55, -46.63 + $i / 100000, 'store', 'green');
}
}
$this->assertSame(10000, $map->count());
$this->assertSame(2, $factory->count());
}
public function testExpectEachMarkerToKeepItsOwnPosition(): void
{
$map = new DeliveryMap(new MarkerStyleFactory());
$map->addMarker(-23.5505, -46.6333, 'truck', 'blue');
$map->addMarker(-22.9068, -43.1729, 'truck', 'blue');
$this->assertSame(
[
'truck (blue) at -23.5505, -46.6333',
'truck (blue) at -22.9068, -43.1729',
],
$map->render()
);
}
}
O último teste mostra que compartilhar o estilo não mistura os marcadores: cada um continua com a sua posição.
Quanto de memória o Flyweight economiza?
Medi a memória usada para criar os marcadores deste exemplo, primeiro com um MarkerStyle novo para cada marcador e depois com a fábrica:
| Marcadores | Um estilo por marcador | Com Flyweight |
|---|---|---|
| 10.000 | 2.226 KB | 1.317 KB |
| 100.000 | 21.279 KB | 12.443 KB |
A redução ficou perto de 41%. Os números mudam conforme a versão do PHP e o tamanho dos objetos, então meça no seu caso antes de decidir.
Um detalhe do PHP: quando você atribui a mesma string a várias variáveis, o motor não copia o valor enquanto ninguém o altera, como explica o manual do PHP. Por isso, neste exemplo o ganho vem de deixar de criar milhares de objetos repetidos.
Quando usar o padrão Flyweight?
- Quando a aplicação cria uma quantidade muito grande de objetos parecidos e a memória virou um problema que você mediu.
- Quando boa parte dos dados desses objetos se repete e pode ser separada do que é próprio de cada um.
- Quando os objetos compartilhados podem ser imutáveis.
Cuidados ao usar o Flyweight
- O flyweight precisa ser imutável. Se um marcador pudesse alterar o estilo compartilhado, alteraria o de todos os outros.
- O código fica mais complicado: aparece uma fábrica, e parte dos dados passa a viajar por parâmetro. Sem um problema real de memória, não compensa.
- Em PHP para web, cada requisição começa do zero e raramente cria milhares de objetos. O padrão faz mais sentido em importações, scripts de linha de comando, filas e processos que ficam rodando.
- A fábrica segura os objetos enquanto ela existir. Em processos longos, isso mantém na memória estilos que talvez ninguém use mais.
Flyweight e Singleton: qual a diferença?
Os dois padrões evitam criar objetos repetidos, e por isso são confundidos. A diferença está em quantas instâncias existem e no que elas podem guardar:
| Padrão | Quantas instâncias | O que a instância guarda |
|---|---|---|
| Flyweight | Uma para cada combinação de dados (aqui, uma por ícone e cor) | Só dados que não mudam, usados por muitos objetos |
| Singleton | Uma única para a classe inteira | Pode guardar dados que mudam |
Esse é mais um padrão estrutural do livro GoF. Para conhecer os outros, veja o artigo Resumo dos Padrões de Projetos.
O código completo deste exemplo, com os testes, está no meu GitHub, na pasta src/Structural/Flyweight do repositório growthdev-repo/design-patterns.
Veja também: o resumo dos 23 padrões de projeto GoF e os outros padrões estruturais já publicados: Adapter, Bridge, Composite, Decorator, Facade, Proxy.
Confiança Sempre!!!
Fontes:
- [1] GAMMA, Erich et al. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos.
- [2] FREEMAN, Eric; ROBSON, Elisabeth; BATES, Bert; SIERRA, Kathy. Head First Design Patterns.
- [3] PHP Manual. Reference Counting Basics.

Continue a conversa