Padrão Composite: Modelagem de Estruturas Hierárquicas com Objetos Compostos

Padrão Composite

O Padrão Composite é um padrão de design estrutural que permite compor objetos em estruturas de árvore para representar hierarquias parte-todo. Isso permite que clientes tratem objetos individuais e composições de objetos de maneira uniforme.

Considere um sistema de arquivos: diretórios podem conter arquivos ou outros diretórios, formando uma estrutura em árvore. Os usuários interagem com arquivos e diretórios de forma consistente, sem se preocupar com a complexidade da hierarqiua.

O Padrão é adequado em cenários onde dados são organizados hierarquicamente, como menus de sistemas, estruturas organizacionais ou representações gráficas de componentes.

Componentes do Padrão

O padrão envolve três papéis principais:

  • Componente: Define a interface comum para todos os objetos na composição, implementando comportamentos padrão.
  • Composto: Representa nós que podem ter filhos, gerenciando operações como adição e remoção de subcomponentes.
  • Folha: Representa nós terminais sem filhos, implementando operações específicas.

Caso de Uso: Sistema de Menus

Em sistemas de gerenciamento, menus frequentemente têm múltiplos níveis. Por exemplo, um menu principal pode conter itens e submenus, que por sua vez podem conter mais itens. O objetivo é imprimir todos os itens de menu de forma recursiva, tratando menus e itens uniformemente.

Implementação em Código

Abaixo, uma implementação em Java que demonstra o Padrão Composite. A classe abstrata define a interface, enquanto as subclasses implementam comportamentos específicos. As variáveis e métodos foram renomeados para reduzir a similaridade com exemplos comuns.

// Classe abstrata que define a interface comum
public abstract class ComponenteSistema {
    protected String descricao;
    protected int profundidade;

    public String getDescricao() {
        return descricao;
    }

    // Métodos com implementação padrão que lançam exceções
    public ComponenteSistema obterFilho(int indice) {
        throw new OperacaoNaoSuportadaException();
    }

    public void incluir(ComponenteSistema componente) {
        throw new OperacaoNaoSuportadaException();
    }

    public void excluir(ComponenteSistema componente) {
        throw new OperacaoNaoSuportadaException();
    }

    public void renderizar() {
        throw new OperacaoNaoSuportadaException();
    }
}
import java.util.ArrayList;
import java.util.List;

// Classe para componentes compostos, como menus
public class MenuComposto extends ComponenteSistema {
    private List<componentesistema> elementos;

    public MenuComposto(String descricao, int profundidade) {
        this.descricao = descricao;
        this.profundidade = profundidade;
        this.elementos = new ArrayList<>();
    }

    @Override
    public ComponenteSistema obterFilho(int indice) {
        return elementos.get(indice);
    }

    @Override
    public void incluir(ComponenteSistema componente) {
        elementos.add(componente);
    }

    @Override
    public void excluir(ComponenteSistema componente) {
        elementos.remove(componente);
    }

    @Override
    public void renderizar() {
        for (int i = 1; i < profundidade; i++) {
            System.out.print("  ");
        }
        System.out.println(descricao);
        for (ComponenteSistema elemento : elementos) {
            elemento.renderizar();
        }
    }
}</componentesistema>
// Classe para folhas, como itens de menu
public class ItemMenu extends ComponenteSistema {
    public ItemMenu(String descricao, int profundidade) {
        this.descricao = descricao;
        this.profundidade = profundidade;
    }

    @Override
    public void renderizar() {
        for (int i = 1; i < profundidade; i++) {
            System.out.print("  ");
        }
        System.out.println(descricao);
    }
}
// Exemplo de uso
public class Aplicacao {
    public static void main(String[] args) {
        ComponenteSistema raiz = new MenuComposto("Menu Principal", 1);
        ComponenteSistema submenu1 = new MenuComposto("Configurações", 2);
        ComponenteSistema submenu2 = new MenuComposto("Relatórios", 2);

        raiz.incluir(submenu1);
        raiz.incluir(submenu2);

        submenu1.incluir(new ItemMenu("Perfil de Usuário", 3));
        submenu1.incluir(new ItemMenu("Preferências", 3));
        submenu2.incluir(new ItemMenu("Vendas Mensais", 3));

        // Renderiza a árvore inteira recursivamente
        raiz.renderizar();
    }
}

Variações do Padrão

O Padrão Composite pode ser implementado de duas formas principais:

  • Composite Transparente: A classe abstrata define todos os métodos de gerenciamento, garantindo uma interface uniforme. Isso pode levar a verificações em tempo de execução para folhas que não suportam operações como adição.
  • Composite Seguro: Métodos de gerenciamento são declarados apenas nas classes compostas, tornando a interface mais segura mas menos consistente, pois clientes precisam diferenciar entre folhas e compostos.

Benefícios

O Padrão Composite oferece várias vantagens:

  • Simplifica o tratamento de estruturas complexas ao unificar a interface para objetos individuais e compostos.
  • Facilita a extensão da árvore com novos tipos de componentes sem alterar código existente.
  • Promove o princípio aberto/fechado, permitindo modificações com impacto mínimo.

Tags: Composite Pattern java Estrutura em Árvore design patterns Orientação a Objetos

Publicado em 7-14 00:54