O Flyweight é um padrão estrutural que permite ajustar mais objetos na quantidade disponível de RAM compartilhando partes comuns de estado entre múltiplos objetos, em vez de manter todos os dados em cada objeto.
- Quando seu programa precisa gerar um grande número de objetos similares
- Quando a RAM é uma restrição crítica no seu sistema
- Quando muitos objetos compartilham propriedades comuns
- Quando você pode extrair o estado intrínseco (compartilhado) do estado extrínseco (único)
from typing import Dict
import json
# Flyweight
class Personagem:
def __init__(self, nome: str, tipo: str, sprites: dict):
# Estado intrínseco - compartilhado
self.tipo = tipo
self.sprites = sprites
self.nome = nome
def renderizar(self, x: int, y: int, estado: str):
# Estado extrínseco - passado pelo contexto
sprite = self.sprites.get(estado, "sprite_padrao")
print(f"Renderizando {self.nome} ({self.tipo}) em x:{x}, y:{y} com sprite: {sprite}")
# FlyweightFactory
class FabricaPersonagens:
_personagens: Dict[str, Personagem] = {}
@classmethod
def obter_personagem(cls, nome: str) -> Personagem:
"""Retorna um personagem existente ou cria um novo se necessário"""
if nome not in cls._personagens:
# Simulando carregamento de dados de um arquivo
dados_personagem = {
"guerreiro": {
"tipo": "guerreiro",
"sprites": {
"parado": "guerreiro_parado.png",
"correndo": "guerreiro_correndo.png",
"atacando": "guerreiro_atacando.png"
}
},
"arqueiro": {
"tipo": "arqueiro",
"sprites": {
"parado": "arqueiro_parado.png",
"correndo": "arqueiro_correndo.png",
"atacando": "arqueiro_atacando.png"
}
},
"mago": {
"tipo": "mago",
"sprites": {
"parado": "mago_parado.png",
"correndo": "mago_correndo.png",
"atacando": "mago_atacando.png"
}
}
}
# Cria novo personagem apenas se necessário
dados = dados_personagem.get(nome)
if dados:
cls._personagens[nome] = Personagem(
nome=nome,
tipo=dados["tipo"],
sprites=dados["sprites"]
)
return cls._personagens.get(nome)
# Contexto
class PersonagemContexto:
def __init__(self, nome: str, x: int, y: int):
# Estado extrínseco
self.x = x
self.y = y
# Referência ao flyweight
self.personagem = FabricaPersonagens.obter_personagem(nome)
def mover(self, novo_x: int, novo_y: int):
self.x = novo_x
self.y = novo_y
self.personagem.renderizar(self.x, self.y, "correndo")
def atacar(self):
self.personagem.renderizar(self.x, self.y, "atacando")
# Exemplo de uso
def main():
# Criando múltiplas instâncias que compartilham dados
personagem1 = PersonagemContexto("guerreiro", 10, 10)
personagem2 = PersonagemContexto("guerreiro", 20, 20)
personagem3 = PersonagemContexto("arqueiro", 30, 30)
personagem4 = PersonagemContexto("mago", 40, 40)
# Usando os personagens
personagem1.mover(15, 15)
personagem2.atacar()
personagem3.mover(35, 35)
personagem4.atacar()
# Verificando o compartilhamento de recursos
print("\nVerificando compartilhamento de recursos:")
print(f"personagem1 e personagem2 compartilham o mesmo flyweight: "
f"{personagem1.personagem is personagem2.personagem}")
print(f"personagem1 e personagem3 compartilham o mesmo flyweight: "
f"{personagem1.personagem is personagem3.personagem}")
if __name__ == "__main__":
main()- Economiza RAM quando o programa precisa de muitos objetos similares
- Centraliza o estado comum em um único lugar
- Reduz o custo de criação de objetos
- Melhora a coerência dos dados compartilhados
- Pode complicar o código devido à separação de estado
- Pode impactar o tempo de execução ao trocar economia de RAM por CPU
- Requer identificação cuidadosa do que pode ser compartilhado
- Pode tornar o código mais difícil de manter
- Identifique claramente os estados intrínseco e extrínseco
- Use uma fábrica para gerenciar os flyweights
- Considere usar cache para flyweights frequentemente usados
- Avalie o trade-off entre memória e desempenho
- Considere usar junto com o padrão Factory Method para criação de flyweights