Padrões de Projetos - Composite
Afluentes: Modelos, métodos e técnicas da engenharia de software
Descrição
O padrão Composite (ou Compósito) é usado para representar hierarquias de objetos que podem ser tratados de maneira uniforme. Ele permite que você organize objetos em estruturas de árvore, onde cada objeto pode ser um componente individual ou um grupo de componentes (compósitos). Com isso, você pode tratar tanto objetos individuais quanto grupos de forma consistente, pois ambos implementam a mesma interface.
Esse padrão é muito útil quando queremos manipular uma estrutura de objetos de forma recursiva, como em menus, diretórios de arquivos, ou representações gráficas com formas e grupos de formas.
Funcionamento do Composite
- Component: Interface ou classe abstrata que define a interface comum para todos os objetos na composição.
- Leaf (Folha): Representa um objeto simples que não contém outros objetos (um objeto “folha”).
- Composite: Representa um objeto que pode conter outros objetos (folhas ou compósitos), e implementa operações para adicionar, remover e manipular seus filhos.
Exemplo em Java
Vamos implementar um exemplo usando o padrão de projeto Composite para desenhar formas como círculos e retângulos, que podem ser organizadas em grupos e tratadas de forma uniforme usando o padrão Composite. Para isso, usaremos o Canvas
do Java Swing e AWT. Usaremos o método paintComponent
de um JPanel
para desenhar no canvas, e os componentes gráficos serão desenhados diretamente no contexto gráfico Graphics
.
Exemplo em Java com Java Swing
- Interface
Forma
: Representa a interface comum para todas as formas, com o métododesenhar
. - Classes
Circulo
eRetangulo
: Representam formas individuais. - Classe
ListaDeFiguras
: Representa um grupo de formas que pode conter outras formas ou grupos.
Classe Figura
Interface comum para todas as formas, com o método desenhar(Graphics g)
.
import java.awt.Graphics;
// Interface comum para todas as formas
public interface Figura {
void desenhar(Graphics g);
}
Classe Círculo
A classe Círculo Implementa Figura
e representa objetos simples (folhas) que desenham um círculo no Graphics
.
import java.awt.*;
// Classe para desenhar um círculo
class Circulo implements Figura {
private int x, y, raio;
private Color cor;
public Circulo(int x, int y, int raio, Color cor) {
this.x = x;
this.y = y;
this.raio = raio;
this.cor = cor;
}
@Override
public void desenhar(Graphics g) {
g.setColor(cor);
g.fillOval(x, y, raio * 2, raio * 2);
}
}
Classe Retangulo
Tal qual a classe Círculo, mas desenha um retângulo.
import java.awt.*;
// Classe para desenhar um retângulo
class Retangulo implements Figura {
private int x, y, largura, altura;
private Color cor;
public Retangulo(int x, int y, int largura, int altura, Color cor) {
this.x = x;
this.y = y;
this.largura = largura;
this.altura = altura;
this.cor = cor;
}
@Override
public void desenhar(Graphics g) {
g.setColor(cor);
g.fillRect(x, y, largura, altura);
}
}
Classe ListaDeFiguras
Implementa Figura
e contém uma lista de Figuras
(folhas ou compostos). Ao chamar desenhar(Graphics g)
, ele delega a chamada para cada forma contida no grupo.
import java.awt.*;
import java.util.List;
import java.util.ArrayList;
// Composto no padrão Composite
class ListaDeFiguras implements Figura {
private List<Figura> formas = new ArrayList<>();
public void adicionar(Figura forma) {
formas.add(forma);
}
public void remover(Figura forma) {
formas.remove(forma);
}
@Override
public void desenhar(Graphics g) {
for (Figura forma : formas) {
forma.desenhar(g);
}
}
}
Classe TelaDeDesenho
Herda JPanel
e sobrepõe o método paintComponent
para desenhar a estrutura de Figura
(composta) no Graphics
.
import java.awt.*;
import javax.swing.JPanel;
// JPanel personalizado que desenha as formas no Canvas
public class TelaDeDesenho extends JPanel {
private Figura formaComposta;
public TelaDeDesenho(Figura formaComposta) {
this.formaComposta = formaComposta;
setPreferredSize(new Dimension(400, 400));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (formaComposta != null) {
formaComposta.desenhar(g);
}
}
}
Classe Main
Código principal do programa que configura a interface Swing
, cria algumas formas, organiza-as em grupos e adiciona o painel ao JFrame
para exibir a composição gráfica.
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
// Criando algumas formas individuais
Figura circulo1 = new Circulo(50, 50, 30, Color.BLUE);
Figura retangulo1 = new Retangulo(150, 70, 80, 40, Color.RED);
// Criando um grupo de formas (Composto)
ListaDeFiguras grupo1 = new ListaDeFiguras();
grupo1.adicionar(circulo1);
grupo1.adicionar(retangulo1);
// Criando outro grupo com novas formas e adicionando grupo1 dentro dele
ListaDeFiguras grupo2 = new ListaDeFiguras();
grupo2.adicionar(grupo1); // Grupo de formas dentro de outro grupo
grupo2.adicionar(new Circulo(200, 200, 40, Color.GREEN));
grupo2.adicionar(new Retangulo(250, 250, 100, 50, Color.PINK));
// Configuração da janela Swing
JFrame frame = new JFrame("Exemplo de Composite");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
// Adiciona o painel de desenho com o grupo de formas
frame.add(new TelaDeDesenho(grupo2));
// Exibe a janela
frame.pack();
frame.setVisible(true);
}
}
Execução
Quando você executa o código, ele abre uma janela do Swing com as formas desenhadas no painel. A estrutura hierárquica permite que você trate todos os objetos como uma única entidade, o que torna o desenho muito flexível e extensível para estruturas mais complexas.
Vantagens do Composite
- Hierarquia Recursiva e Flexível:
- Permite criar hierarquias recursivas de objetos de forma que componentes simples e compostos sejam tratados de maneira uniforme. Isso facilita o trabalho com estruturas complexas, como árvores, onde cada elemento pode ser outro grupo de elementos.
- Simplicidade para o Cliente:
- O cliente que usa os objetos Composite não precisa se preocupar em diferenciar objetos simples de compostos. Ele interage com uma interface única e pode manipular todos os elementos como se fossem homogêneos.
- Facilidade de Expansão:
- Como novos tipos de componentes podem ser adicionados sem modificar o código existente, o Composite promove a extensibilidade. Basta adicionar novos tipos de
Leaf
ouComposite
, e eles poderão ser usados de forma transparente na estrutura hierárquica.
- Como novos tipos de componentes podem ser adicionados sem modificar o código existente, o Composite promove a extensibilidade. Basta adicionar novos tipos de
- Reduz Complexidade de Código:
- Ao tratar elementos simples e compostos de forma uniforme, o padrão Composite ajuda a simplificar o código cliente, reduzindo condicionais e verificações para identificar o tipo de objeto.
Desvantagens do Composite
- Dificuldade de Implementação de Restrições:
- Em algumas situações, pode ser necessário restringir certos componentes para serem compostos ou folhas. O Composite não diferencia explicitamente esses tipos, o que pode dificultar a implementação de restrições na hierarquia (como, por exemplo, evitar que uma folha contenha outras folhas).
- Complexidade de Manutenção:
- Em estruturas hierárquicas muito grandes e complexas, o Composite pode levar a estruturas de objetos profundas e difíceis de gerenciar, o que pode impactar a performance e aumentar a dificuldade de depuração.
- Performance:
- O Composite pode adicionar uma sobrecarga de processamento em hierarquias muito grandes, pois a execução de operações (como
desenhar()
ouadicionar()
) em uma estrutura complexa pode envolver múltiplas chamadas recursivas ou iterações por vários objetos.
- O Composite pode adicionar uma sobrecarga de processamento em hierarquias muito grandes, pois a execução de operações (como
- Viola o Princípio de Responsabilidade Única:
- Um Composite geralmente possui tanto a lógica para o comportamento de seus elementos quanto para a sua organização hierárquica (adicionar, remover, gerenciar filhos). Isso pode levar a um acúmulo de responsabilidades, o que vai contra o princípio de responsabilidade única.
Quando Usar o Composite
O padrão Composite é mais adequado quando:
- Você tem uma estrutura hierárquica de objetos, como uma árvore.
- Deseja tratar de forma uniforme objetos simples e composições de objetos.
- Precisa adicionar elementos na estrutura sem modificar o código cliente.
Resumo
O Composite é poderoso para lidar com hierarquias de objetos, mas deve ser usado com atenção em projetos complexos para evitar problemas de performance e de organização.