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.