Padrões Flyweight e Composite | Introdução
No desenvolvimento de software, a eficiência e o gerenciamento de recursos são cruciais, especialmente ao lidar com sistemas que apresentam um grande número de objetos.
Nesse contexto, a escolha de padrões de projeto adequados pode fazer uma diferença significativa na performance e na manutenibilidade do sistema.
Entre os padrões estruturais, que se concentram na forma como classes e objetos são compostos, destacam-se os padrões Flyweight e Composite, ambos oferecendo soluções eficazes para esses desafios (SABBAG FILHO, 2024).
O padrão Flyweight é usado para minimizar o uso de memória ao compartilhar objetos, enquanto o padrão Composite permite tratar objetos individuais e composições de objetos de maneira uniforme.
Este artigo explora em detalhes a aplicação combinada desses dois padrões, fornecendo exemplos práticos e explicações sobre como eles podem ser utilizados para otimizar o desempenho de sistemas complexos.
A integração dos padrões Flyweight e Composite é especialmente benéfica em cenários onde a criação de muitos objetos é necessária, como em jogos, aplicações gráficas e sistemas de gerenciamento de dados.
O Padrão Flyweight
O padrão Flyweight é um padrão estrutural que busca otimizar o uso da memória ao compartilhar objetos que possuem estado comum, reduzindo a sobrecarga de instâncias redundantes (REFACTORING GURU, 2025).
Ele é particularmente útil em sistemas onde muitos objetos precisam ser criados e mantidos, principalmente onde muitos desses objetos compartilham características comuns.
O Flyweight permite que você crie um grande número de instâncias de objetos, economizando memória ao compartilhar os dados que permanecem constantes (SARCAR, 2022).
Um exemplo clássico do padrão Flyweight é a representação de caracteres em um editor de texto.
Ao invés de criar uma nova instância para cada letra, podemos ter instâncias compartilhadas para cada tipo de letra.
Essa abordagem não só economiza memória, mas também melhora a performance ao minimizar o número de objetos na heap.
using System;
using System.Collections.Generic;
// Flyweight
public interface ICharacter
{
void Display(int x, int y);
}
public class Character : ICharacter
{
private readonly char _letter;
public Character(char letter)
{
_letter = letter;
}
public void Display(int x, int y)
{
Console.WriteLine($"Displaying letter '{_letter}' at position ({x}, {y})");
}
}
public class CharacterFactory
{
private readonly Dictionary<char, ICharacter> _characters = new Dictionary<char, ICharacter>();
public ICharacter GetCharacter(char letter)
{
if (!_characters.ContainsKey(letter))
{
_characters[letter] = new Character(letter);
}
return _characters[letter];
}
}
class Program
{
static void Main()
{
var factory = new CharacterFactory();
var text = "Hello, World!";
for (int i = 0; i < text.Length; i++)
{
var character = factory.GetCharacter(text[i]);
character.Display(i * 10, 0);
}
}
}
O Padrão Composite
O padrão Composite é um padrão estrutural que permite compor objetos em estruturas de árvore para representar hierarquias parte-todo.
Ele permite que os clientes tratem objetos individuais e composições de objetos de maneira uniforme, facilitando a manipulação de estruturas complexas (MANCHANA, 2019).
Essa uniformidade é especialmente útil em sistemas onde é necessário realizar operações em grupos de objetos que têm relação hierárquica.
Um exemplo prático do padrão Composite pode ser encontrado em sistemas de gerenciamento de arquivos, onde diretórios podem conter arquivos e outros diretórios.
Isso permite que a estrutura seja tratada como um único objeto, simplificando operações como exibição, remoção ou adição de elementos.
using System;
using System.Collections.Generic;
// Component
public abstract class Component
{
public abstract void Display(int depth);
}
// Leaf
public class File : Component
{
private readonly string _name;
public File(string name)
{
_name = name;
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + _name);
}
}
// Composite
public class Directory : Component
{
private readonly string _name;
private readonly List<Component> _components = new List<Component>();
public Directory(string name)
{
_name = name;
}
public void Add(Component component)
{
_components.Add(component);
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + _name);
foreach (var component in _components)
{
component.Display(depth + 2);
}
}
}
class Program
{
static void Main()
{
var root = new Directory("Root");
var folder1 = new Directory("Folder1");
var folder2 = new Directory("Folder2");
root.Add(folder1);
root.Add(folder2);
folder1.Add(new File("File1.txt"));
folder1.Add(new File("File2.txt"));
folder2.Add(new File("File3.txt"));
root.Display(1);
}
}
Integração dos Padrões Flyweight e Composite
A combinação dos padrões Flyweight e Composite oferece uma solução poderosa para sistemas que lidam com grandes quantidades de objetos que têm tanto um estado compartilhado quanto uma estrutura hierárquica.
Um exemplo prático pode ser encontrado em jogos, onde múltiplos objetos de um mesmo tipo (como inimigos ou itens) devem ser gerenciados em uma cena, mas também organizados em uma hierarquia de níveis. Abaixo um exemplo prático:
Ao integrar esses padrões, conseguimos não apenas reduzir o uso de memória ao compartilhar instâncias de objetos comuns, mas também organizar esses objetos em uma estrutura hierárquica que facilita a manipulação e a interação entre eles.
Essa abordagem é especialmente útil em cenários onde a performance é crítica, como em jogos em tempo real ou aplicações gráficas complexas. Abaixo um exemplo da sua aplicação:
using System;
using System.Collections.Generic;
public interface IEnemy
{
void Attack();
}
public class Enemy : IEnemy
{
private readonly string _type;
public Enemy(string type)
{
_type = type;
}
public void Attack()
{
Console.WriteLine($"Enemy of type {_type} attacking!");
}
}
public class EnemyFactory
{
private readonly Dictionary<string, IEnemy> _enemies = new Dictionary<string, IEnemy>();
public IEnemy GetEnemy(string type)
{
if (!_enemies.ContainsKey(type))
{
_enemies[type] = new Enemy(type);
}
return _enemies[type];
}
}
public class Level
{
private readonly List<IEnemy> _enemies = new List<IEnemy>();
public void AddEnemy(IEnemy enemy)
{
_enemies.Add(enemy);
}
public void Attack()
{
foreach (var enemy in _enemies)
{
enemy.Attack();
}
}
}
class Program
{
static void Main()
{
var factory = new EnemyFactory();
var level1 = new Level();
level1.AddEnemy(factory.GetEnemy("Orc"));
level1.AddEnemy(factory.GetEnemy("Orc"));
level1.AddEnemy(factory.GetEnemy("Goblin"));
level1.Attack();
}
}
Vantagens da Abordagem Combinada
A aplicação dos padrões Flyweight e Composite em conjunto resulta em uma arquitetura que reduz significativamente o uso de memória, ao mesmo tempo que mantém uma estrutura clara e fácil de modificar.
Essa abordagem é especialmente vantajosa em cenários de jogos, simulações e sistemas de gráficos complexos, onde a performance e a organização são essenciais.
Além disso, ao permitir a criação de uma hierarquia de objetos, facilita a extensão e a personalização do sistema, permitindo que novos tipos de objetos sejam adicionados sem alterar o comportamento existente.
Outro ponto importante é a manutenção do código.
A utilização desses padrões pode levar a um design mais limpo, onde as responsabilidades estão bem definidas, facilitando a identificação de problemas e a realização de alterações.
Isso é crucial em projetos de longo prazo, onde a adaptação e a evolução do software são inevitáveis.
Considerações Finais
O uso combinado dos padrões Flyweight e Composite é uma estratégia eficaz para lidar com sistemas que possuem muitos objetos.
Ao permitir o compartilhamento de estados comuns e a criação de hierarquias de objetos, esses padrões proporcionam uma solução robusta e eficiente para desafios de desempenho e gerenciamento de memória.
A compreensão e a aplicação correta desses padrões podem levar a um design de software mais limpo, eficiente e escalável.
Em um mundo onde a complexidade dos sistemas de software continua a crescer, a adoção de práticas de design eficazes como estas se torna cada vez mais importante.
Referências
- SABBAG FILHO, Nagib. Comparative Analysis of Patterns: Distinctions and Applications of Behavioral, Creational, and Structural Patterns. Leaders Tec, v. 1, n. 11, 2024.
- SARCAR, Vaskaran. Padrão Flyweight. Em: Padrões de Design Java: Uma Experiência Prática com Exemplos do Mundo Real . Berkeley, CA: Apress, 2022. p. 263-282.
- REFACTORING GURU. Flyweight. Disponível em: https://refactoring.guru/design-patterns/flyweight. Acesso em: 28 mai. 2025.
- MANCHANA, Ramakrishna. Structural Design Patterns: Composing Efficient and Scalable Software Architectures. International Journal of Scientific Research and Engineering Trends, v. 5, p. 1483-1491, 2019.