Capítulo 08: Enumerações Avançadas e Modelagem por Composição de Objetos
Especialização em Sistemas com Java 21 LTS • Módulo de Modelagem Orientada a Objetos
🗺️ Mapa Conceitual do Tópico
classDiagram
class Pedido {
-String codigo
-LocalDateTime dataHora
-StatusPedido status
-Cliente cliente
-List~ItemPedido~ itens
+adicionarItem(Produto p, int qtd) void
+removerItem(String sku) void
+calcularTotal() BigDecimal
+avancarStatus() void
}
class Cliente {
-String cpf
-String nome
-String email
}
class ItemPedido {
-Produto produto
-int quantidade
-BigDecimal precoUnitario
+subTotal() BigDecimal
}
class Produto {
-String sku
-String nome
-BigDecimal precoBase
}
class StatusPedido {
<<enumeration>>
AGUARDANDO_PAGAMENTO
PAGO
ENVIADO
ENTREGUE
CANCELADO
+podeTransicionarPara(StatusPedido novo) boolean
}
Pedido *-- ItemPedido : "Composição Forte (1..*)"
Pedido o-- Cliente : "Agregação (1..1)"
ItemPedido o-- Produto : "Referência (1..1)"
Pedido --> StatusPedido : "Estado do Domínio"
🏷️ 1. Enumerações Avançadas em Java (enum)
Em Java, um enum não é um mero conjunto de constantes numéricas (como em C++ ou C#), mas uma classe especializada de tipo seguro derivada implicitamente de java.lang.Enum<E>.
Características Notáveis:
- Instâncias Únicas (Singletons): A JVM garante que cada constante do enum seja instanciada exatamente uma única vez durante o carregamento da classe.
- Atributos e Construtores: Enums podem conter variáveis de instância
finale construtores obrigatoriamenteprivate. - Comportamentos Abstratos Polimórficos: Um enum pode declarar métodos abstratos que são obrigatoriamente implementados por cada constante, constituindo uma implementação elegante do padrão de projeto Strategy:
public enum TipoDesconto {
NENHUM {
@Override
public BigDecimal aplicar(BigDecimal valor) { return valor; }
},
FIDELIDADE {
@Override
public BigDecimal aplicar(BigDecimal valor) { return valor.multiply(new BigDecimal("0.90")); }
},
BLACK_FRIDAY {
@Override
public BigDecimal aplicar(BigDecimal valor) { return valor.multiply(new BigDecimal("0.70")); }
};
public abstract BigDecimal aplicar(BigDecimal valor);
}
⚠️ Regra de Ouro: Nunca persista o .ordinal()
O método .ordinal() retorna o índice da constante baseado na ordem de declaração (0, 1, 2…). Se um desenvolvedor alterar a posição das constantes ou inserir uma nova no início, todos os registros pré-existentes no banco de dados serão corrompidos. Persista sempre o nome textual (.name()) ou um código de negócio fixo.
🧩 2. O Princípio Arquitetural: “Favor Composition Over Inheritance”
A composição é uma relação estrutural onde um objeto “tem-um” (has-a) outro objeto como parte de seu estado interno, ao invés de “ser-um” (is-a) através de herança hierárquica.
Por Que a Composição é Superior à Herança em Modelagem Corporativa:
- Desacoplamento: A herança quebra o encapsulamento porque a subclasse depende dos detalhes internos da superclasse (Fragile Base Class Problem). A composição interage unicamente através de contratos públicos.
- Flexibilidade em Tempo de Execução: Comportamentos compostos podem ser injetados ou substituídos dinamicamente sem alterar a estrutura de classes.
- Coesão Limpa: Cada classe foca exclusivamente em sua responsabilidade única (SRP), delegando tarefas especializadas para suas partes componentes.
📐 3. Composição Forte vs Agregação Fraca
No padrão UML e na modelagem em código Java:
- Composição Forte (
*--): As partes pertencem exclusivamente ao objeto “todo”. Se oPedidofor cancelado ou apagado da memória, seusItemPedidoperdem a razão de existir e são coletados pelo GC. - Agregação Fraca (
o--): As partes possuem ciclo de vida independente. Se oPedidodeixar de existir, oClientecontinua existindo no banco de dados e na memória do sistema.
📊 4. Matriz Comparativa: Composição vs Agregação vs Herança
| Dimensão | Composição Forte | Agregação Fraca | Herança de Classes |
|---|---|---|---|
| Relação Semântica | “Tem-um” (parte inseparável) | “Tem-um” (relação contextual) | “É-um” (especialização de tipo) |
| Ciclo de Vida | Vinculado e dependente | Totalmente independente | Acoplado em tempo de compilação |
| Acoplamento | Baixo | Mínimo | Muito Alto |
| Reaproveitamento | Por delegação de chamadas | Por delegação de chamadas | Por extensão de código |
| Multiplicidade | 1 para 1 ou 1 para N | 1 para 1 ou 1 para N | Estritamente simples em Java (extends) |
💻 5. Código de Demonstração Corporativo (Java 21 LTS)
package com.empresa.composicao;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Sistema de Pedidos corporativo demonstrando composição de entidades e ciclo de vida de enums.
*/
public class DominioVendasComposicao {
public enum StatusPedido {
CRIADO {
@Override
public boolean podeAvancarPara(StatusPedido proximo) { return proximo == PAGO || proximo == CANCELADO; }
},
PAGO {
@Override
public boolean podeAvancarPara(StatusPedido proximo) { return proximo == ENVIADO || proximo == CANCELADO; }
},
ENVIADO {
@Override
public boolean podeAvancarPara(StatusPedido proximo) { return proximo == ENTREGUE; }
},
ENTREGUE {
@Override
public boolean podeAvancarPara(StatusPedido proximo) { return false; }
},
CANCELADO {
@Override
public boolean podeAvancarPara(StatusPedido proximo) { return false; }
};
public abstract boolean podeAvancarPara(StatusPedido proximo);
}
public record Cliente(String id, String nome, String email) {}
public record Produto(String sku, String titulo, BigDecimal preco) {}
public static class ItemPedido {
private final Produto produto;
private final int quantidade;
private final BigDecimal precoUnitario;
public ItemPedido(Produto produto, int quantidade) {
this.produto = produto;
this.quantidade = quantidade;
this.precoUnitario = produto.preco();
}
public BigDecimal calcularSubtotal() {
return precoUnitario.multiply(new BigDecimal(quantidade));
}
public Produto getProduto() { return produto; }
public int getQuantidade() { return quantidade; }
public BigDecimal getPrecoUnitario() { return precoUnitario; }
}
public static class Pedido {
private final String codigoPedido;
private final LocalDateTime dataCriacao;
private final Cliente cliente; // Agregação
private final List<ItemPedido> itens = new ArrayList<>(); // Composição forte
private StatusPedido status;
public Pedido(String codigoPedido, Cliente cliente) {
this.codigoPedido = codigoPedido;
this.cliente = cliente;
this.dataCriacao = LocalDateTime.now();
this.status = StatusPedido.CRIADO;
}
public void adicionarItem(Produto produto, int quantidade) {
if (this.status != StatusPedido.CRIADO) {
throw new IllegalStateException("Não é possível alterar itens de um pedido com status: " + status);
}
itens.add(new ItemPedido(produto, quantidade));
}
public void transicionarPara(StatusPedido novoStatus) {
if (!this.status.podeAvancarPara(novoStatus)) {
throw new IllegalStateException(String.format("Transição ilegal: de %s para %s", status, novoStatus));
}
this.status = novoStatus;
}
public BigDecimal calcularTotal() {
return itens.stream()
.map(ItemPedido::calcularSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
public String getCodigoPedido() { return codigoPedido; }
public Cliente getCliente() { return cliente; }
public StatusPedido getStatus() { return status; }
public List<ItemPedido> getItens() { return Collections.unmodifiableList(itens); } // Cópia defensiva
}
public static void main(String[] args) {
Cliente cli = new Cliente("CLI-102", "Mariana Vasconcelos", "mariana@email.com");
Pedido pedido = new Pedido("PED-2026-001", cli);
pedido.adicionarItem(new Produto("SKU-NOTE", "MacBook Pro M3", new BigDecimal("14500.00")), 1);
pedido.adicionarItem(new Produto("SKU-MOUSE", "Mouse Magic 3", new BigDecimal("750.00")), 2);
System.out.println("================================================================================");
System.out.printf("Pedido: %s | Cliente: %s | Total: R$ %.2f%n",
pedido.getCodigoPedido(), pedido.getCliente().nome(), pedido.calcularTotal());
System.out.printf("Status Atual: %s%n", pedido.getStatus());
pedido.transicionarPara(StatusPedido.PAGO);
System.out.printf("Status Atualizado: %s%n", pedido.getStatus());
pedido.transicionarPara(StatusPedido.ENVIADO);
System.out.printf("Status Atualizado: %s%n", pedido.getStatus());
System.out.println("================================================================================");
}
}