Padrão Composite
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 75 de 87.
O Composite Pattern é um padrão de projeto 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 por meio de uma interface comum.
Pense em um sistema de arquivos: uma pasta pode conter arquivos e outras pastas, que podem conter mais arquivos e pastas. Quer você esteja lidando com um único arquivo ou com uma estrutura de pastas inteira, você deseja realizar operações como "get size" da mesma maneira. O padrão define uma interface Component, objetos Leaf (elementos individuais) e objetos Composite (contêineres que contêm filhos):
interface FileComponent {
void display(String indent);
int getSize();
}
class File implements FileComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public void display(String indent) {
System.out.println(indent + name + " (" + size + "KB)");
}
public int getSize() {
return size;
}
}
class Folder implements FileComponent {
private String name;
private List<FileComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileComponent component) {
children.add(component);
}
public void display(String indent) {
System.out.println(indent + name + "/");
for (FileComponent child : children) {
child.display(indent + " ");
}
}
public int getSize() {
return children.stream().mapToInt(FileComponent::getSize).sum();
}
}O composite delega operações aos seus filhos, permitindo estruturas recursivas. Os clientes interagem com a árvore sem saber se estão trabalhando com uma folha ou um composite:
Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));
Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);
root.display("");
System.out.println("Total: " + root.getSize() + "KB");O Padrão Composite é ideal para representar hierarquias como organogramas, componentes de UI ou sistemas de menu, onde você precisa tratar grupos e indivíduos de forma idêntica.
Desafio
FácilVamos construir um sistema de organograma usando o Composite Pattern! Você criará uma estrutura onde departamentos podem conter funcionários e outros subdepartamentos, permitindo calcular salários totais e exibir a hierarquia de forma uniforme — quer você esteja olhando para um único funcionário ou para uma divisão inteira.
Você organizará seu código em quatro arquivos:
OrganizationComponent.java: Defina a interface do componente que tanto os funcionários quanto os departamentos implementarão. Ela deve declarar dois métodos:showDetails(String indent)para exibir o componente com a indentação adequada, egetSalary()que retorna o salário total como um número inteiro.Employee.java: Crie a classe folha (leaf) que representa trabalhadores individuais. UmEmployeepossui um nome (String) e salário (int), ambos definidos através do construtor. QuandoshowDetailsé chamado, ele deve imprimir[indent][name]: $[salary]. O métodogetSalarysimplesmente retorna o salário do funcionário.Department.java: Crie a classe composta (composite) que pode conter tanto funcionários quanto subdepartamentos. UmDepartmentpossui um nome (String) e mantém uma lista de filhos do tipoOrganizationComponent. Ele deve ter um métodoadd(OrganizationComponent component)para adicionar membros. QuandoshowDetailsé chamado, ele imprime[indent][name] Department, e então chamashowDetailsem cada filho com indentação aumentada (adicione dois espaços). O métodogetSalaryretorna a soma dos salários de todos os filhos.Main.java: Construa sua organização! Você receberá quatro entradas: dois nomes de funcionários com seus salários, e um nome de subdepartamento com um funcionário.Crie um departamento "Engineering" como sua raiz. Adicione dois funcionários a ele usando os dois primeiros pares de nome/salário. Em seguida, crie um subdepartamento usando a terceira entrada, adicione um funcionário a ele usando o quarto par de nome/salário e adicione este subdepartamento ao Engineering.
Chame
showDetails("")no departamento Engineering e, em seguida, imprima o salário total no formatoTotal Salary: $[amount].
Você receberá as entradas nesta ordem: nome do funcionário1 (String), salário do funcionário1 (int), nome do funcionário2 (String), salário do funcionário2 (int), nome do subdepartamento (String), nome do funcionário3 (String), salário do funcionário3 (int).
Por exemplo, com as entradas Alice, 75000, Bob, 65000, QA, Charlie, 55000, sua saída seria:
Engineering Department
Alice: $75000
Bob: $65000
QA Department
Charlie: $55000
Total Salary: $195000Observe como os mesmos métodos showDetails e getSalary funcionam perfeitamente, sejam chamados para um único funcionário ou para um departamento inteiro contendo subdepartamentos aninhados. A estrutura composta lida com a recursão automaticamente, permitindo que você trate toda a hierarquia de forma uniforme!
Folha de consulta
O Composite Pattern (Padrão de Composição) é um padrão de design estrutural que compõe objetos em estruturas de árvore para representar hierarquias parte-todo. Ele permite tratar objetos individuais e composições de forma uniforme através de uma interface comum.
O padrão consiste em três elementos-chave:
- Component: Interface que define operações comuns
- Leaf: Objetos individuais que implementam a interface do componente
- Composite: Objetos de contêiner que contêm filhos e delegam operações a eles
Exemplo de implementação de um sistema de arquivos:
// Component interface
interface FileComponent {
void display(String indent);
int getSize();
}
// Leaf class
class File implements FileComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public void display(String indent) {
System.out.println(indent + name + " (" + size + "KB)");
}
public int getSize() {
return size;
}
}
// Composite class
class Folder implements FileComponent {
private String name;
private List<FileComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileComponent component) {
children.add(component);
}
public void display(String indent) {
System.out.println(indent + name + "/");
for (FileComponent child : children) {
child.display(indent + " ");
}
}
public int getSize() {
return children.stream().mapToInt(FileComponent::getSize).sum();
}
}Usando a estrutura composta:
Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));
Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);
root.display("");
System.out.println("Total: " + root.getSize() + "KB");O composite delega operações aos seus filhos recursivamente. Os clientes interagem com a árvore sem saber se estão trabalhando com uma folha (leaf) ou uma composição (composite).
Casos de uso: Organogramas, componentes de interface do usuário (UI), sistemas de menu, sistemas de arquivos — qualquer hierarquia onde grupos e indivíduos precisem de tratamento idêntico.
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler as entradas
String emp1Name = scanner.nextLine();
int emp1Salary = Integer.parseInt(scanner.nextLine());
String emp2Name = scanner.nextLine();
int emp2Salary = Integer.parseInt(scanner.nextLine());
String subDeptName = scanner.nextLine();
String emp3Name = scanner.nextLine();
int emp3Salary = Integer.parseInt(scanner.nextLine());
// TODO: Criar o departamento "Engineering" como a raiz
// TODO: Criar e adicionar dois funcionários a Engineering usando os dados de emp1 e emp2
// TODO: Criar um subdepartamento usando subDeptName
// TODO: Criar e adicionar um funcionário ao subdepartamento usando os dados de emp3
// TODO: Adicionar o subdepartamento a Engineering
// TODO: Chamar showDetails("") no departamento Engineering
// TODO: Imprimir o salário total no formato: Total Salary: $[amount]
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator