Padrão Composite
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy. Lição 75 de 87.
O padrão Composite é 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. Seja lidando com um único arquivo ou com uma estrutura de pastas inteira, você quer realizar operações como "obter tamanho" da mesma maneira. O padrão define uma interface Component, objetos Leaf (elementos individuais) e objetos Composite (contêineres que contêm elementos 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 composto delega as 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 composto:
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, nos quais você precisa tratar grupos e indivíduos de forma idêntica.
Desafio
FácilVamos criar um sistema de organograma usando o padrão Composite! Você criará uma estrutura na qual os departamentos podem conter funcionários e outros subdepartamentos, permitindo calcular os salários totais e exibir a hierarquia de maneira uniforme, seja ao visualizar um único funcionário ou 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 o recuo adequado egetSalary(), que retorna o salário total como um inteiro.Employee.java: Crie a classe folha que representa trabalhadores individuais. UmEmployeetem um nome (String) e um salário (int), ambos definidos por meio 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 que pode conter tanto funcionários quanto subdepartamentos. UmDepartmenttem um nome (String) e mantém uma lista de filhosOrganizationComponent. Ele deve ter um métodoadd(OrganizationComponent component)para adicionar membros. QuandoshowDetailsé chamado, ele imprime[indent][name] Departmente, em seguida, chamashowDetailsem cada filho com o recuo aumentado (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 o nome de um 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 esse subdepartamento a 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 de employee1 (String), salário de employee1 (int), nome de employee2 (String), salário de employee2 (int), nome do subdepartamento (String), nome de employee3 (String), salário de employee3 (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 em um único funcionário ou em um departamento inteiro contendo subdepartamentos aninhados. A estrutura composta cuida da recursão automaticamente, permitindo tratar toda a hierarquia de maneira uniforme!
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler 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: Create the "Engineering" department as the root
// TODO: Crie e adicione dois funcionários a Engineering usando os dados de emp1 e emp2
// TODO: Crie um subdepartamento usando subDeptName
// TODO: Crie e adicione um funcionário ao subdepartamento usando os dados de emp3
// TODO: Adicione o subdepartamento a Engineering
// TODO: Call showDetails("") on the Engineering department
// TODO: Imprima 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 IteratorPratique por conta própria: Compilador de Java online