Padrão de Projeto Compositor: Estruturando Hierarquias com Objetos

O padrão de projeto Compositor (Composite) é um padrão estrutural que visa organizar objetos em estruturas de árvore para representar relações de "todo-parte". Sua principal vantagem é permitir que clientes tratem objetos individuais e composições de objetos de forma uniforme, sem a necessidade de distinguir entre eles.

Componentes do Padrão Compositor

Este padrão é composto por três elementos essenciais:

  1. Componente (Component): Define a interface comum para todos os objetos na estrutura da árvore, sejam eles elementos individuais ou contêineres. Pode ser uma classe abstrata ou uma interface, frequentemente fronecendo implementações padrão para operações comuns.
  2. Folha (Leaf): Representa os objetos nas extremidades da árvore, que não possuem filhos. Implementa a interface do Componente e define o comportamento específico para elementos individuais.
  3. Composto (Composite): Representa um objeto que pode conter outros Componentes (sejam Folhas ou outros Compostos). Ele gerencia sua coleção de filhos e delega operações aos seus componentes.

Funcionamento

O padrão Compositor opera da seguinte maneira:

  • Todos os objetos na hierarquia compartilham a mesma interface definida pelo Componente.
  • Objetos Folha implementam essa interface e suas operações, sem gerenciar subcomponentes.
  • Objetos Composto também implementam a interface do Componente, mas adicionalmente gerenciam uma lista de Componentes filhos. Eles podem adicionar, remover e iterar sobre seus filhos, delegando chamadas para eles.
  • O cliente interage com a estrutura através da interface comum do Componente, tratando tanto objetos simples quanto estruturas complexas da mesma forma.

Benefícios

  • Uniformidade: Simplifica o código do cliente ao permitir que ele opere em objetos individuais e composições de maneira idêntica.
  • Extensibilidade e Manutenibilidade: Facilita a adição de novos tipos de componentes sem afetar o código existente que os utiliza. A manipulação de estruturas complexas se torna mais organizada.
  • Hierarquias Complexas: Permite a construção de estruturas de dados recursivas e hierárquicas de forma elegante.

Quando Utilizar

O padrão Compositor é ideal quando:

  • É necessário representar uma hierarquia de objetos onde a relação "todo-parte" é predominante.
  • O cliente precisa tratar um objeto individual e uma composição de objetos da mesma forma.
  • As operações aplicadas aos objetos da hierarquia devem ser transparentes em relação ao seu nível (individual ou composto).

Exemplo de Implementação (C#)

Interface do Componente

Define a interface base para todos os elementos da árvore.


   /// <summary>
   /// Define a interface para todos os elementos na hierarquia do componente.
   /// </summary>
   public abstract class HierarquiaComponente
   {
       protected string nome;

       public HierarquiaComponente(string nomeComponente)
       {
           nome = nomeComponente;
       }

       /// <summary>
       /// Adiciona um filho a este componente.
       /// </summary>
       public virtual void AdicionarFilho(HierarquiaComponente componente)
       {
           // Implementação padrão pode lançar exceção ou não fazer nada.
           throw new NotSupportedException("Este componente não suporta adição de filhos.");
       }

       /// <summary>
       /// Remove um filho deste componente.
       /// </summary>
       public virtual void RemoverFilho(HierarquiaComponente componente)
       {
           // Implementação padrão pode lançar exceção ou não fazer nada.
           throw new NotSupportedException("Este componente não suporta remoção de filhos.");
       }

       /// <summary>
       /// Exibe a estrutura e o nome do componente, com indentação baseada na profundidade.
       /// </summary>
       public abstract void Exibir(int profundidade);
   }
   

Componente Composto (Composite)

Representa um nó que pode conter outros componentes.


   /// <summary>
   /// Representa um nó composto que pode conter outros componentes (folhas ou compostos).
   /// </summary>
   public class Composito : HierarquiaComponente
   {
       private List<HierarquiaComponente> filhos = new List<HierarquiaComponente>();

       public Composito(string nomeComponente) : base(nomeComponente)
       {
       }

       public override void AdicionarFilho(HierarquiaComponente componente)
       {
           filhos.Add(componente);
       }

       public override void RemoverFilho(HierarquiaComponente componente)
       {
           filhos.Remove(componente);
       }

       public override void Exibir(int profundidade)
       {
           Console.WriteLine(new String('-', profundidade) + $"[Composto] {nome}");
           foreach (var filho in filhos)
           {
               filho.Exibir(profundidade + 1);
           }
       }
   }
   

Componente Folha (Leaf)

Representa um elemento individual sem filhos.


   /// <summary>
   /// Representa um elemento individual (folha) na hierarquia. Não possui filhos.
   /// </summary>
   public class Folha : HierarquiaComponente
   {
       public Folha(string nomeComponente) : base(nomeComponente)
       {
       }

       // Métodos AdicionarFilho e RemoverFilho lançam exceção pois folhas não podem ter filhos.
       public override void AdicionarFilho(HierarquiaComponente componente)
       {
           throw new InvalidOperationException("Um componente Folha não pode ter filhos.");
       }

       public override void RemoverFilho(HierarquiaComponente componente)
       {
           throw new InvalidOperationException("Um componente Folha não pode remover filhos.");
       }

       public override void Exibir(int profundidade)
       {
           Console.WriteLine(new String('-', profundidade) + $"[Folha] {nome}");
       }
   }
   

Cliente

Demonstra como o cliente interage com a estrutura hierárquica.


   /// <summary>
   /// Classe cliente que utiliza o padrão Compositor.
   /// </summary>
   public class ClienteApp
   {
       public void Executar()
       {
           // Cria folhas (elementos individuais)
           var itemFilme1 = new Folha("O Poderoso Chefão");
           var itemFilme2 = new Folha("Pulp Fiction");
           var itemFilme3 = new Folha("Batman: O Cavaleiro das Trevas");

           // Cria nós compostos (diretórios/categorias)
           var categoriaDrama = new Composito("Filmes de Drama");
           categoriaDrama.AdicionarFilho(itemFilme1);

           var categoriaAcao = new Composito("Filmes de Ação");
           categoriaAcao.AdicionarFilho(itemFilme3);

           var categoriaVariados = new Composito("Filmes Variados");
           categoriaVariados.AdicionarFilho(itemFilme2);

           // Monta a estrutura hierárquica principal
           var catalogoCompleto = new Composito("Catálogo Principal");
           catalogoCompleto.AdicionarFilho(categoriaDrama);
           catalogoCompleto.AdicionarFilho(categoriaAcao);
           catalogoCompleto.AdicionarFilho(categoriaVariados);

           // O cliente exibe a estrutura inteira sem se preocupar com o nível de cada item.
           Console.WriteLine("Exibindo o catálogo:");
           catalogoCompleto.Exibir(1);

           // Exemplo adicional: Adicionando um novo filme a uma categoria existente
           var novoFilmeAcao = new Folha("Mad Max: Estrada da Fúria");
           categoriaAcao.AdicionarFilho(novoFilmeAcao);

           Console.WriteLine("\nExibindo o catálogo após adicionar novo filme de ação:");
           catalogoCompleto.Exibir(1);
       }
   }

   // Ponto de entrada principal da aplicação
   public class Program
   {
       static void Main(string[] args)
       {
           var cliente = new ClienteApp();
           cliente.Executar();

           Console.WriteLine("\nPressione qualquer tecla para sair...");
           Console.ReadKey();
       }
   }
   

Tags: padrão de projeto compositor estrutural Orientação a Objetos java

Publicado em 7-10 23:43