💻 Exemplos: Capítulo 08: Enumerações Avançadas e Modelagem por Composição de Objetos
Especialização em Sistemas com Java 21 LTS • Capítulo 08
🎯 Visão Geral dos Exemplos Práticos
Neste laboratório prático, implementaremos exemplos corporativos completos explorando:
- Composição Forte e Agregação UML: modelagem de um ecossistema de checkout (
Pedido,Cliente,ItemPedidoeProduto). - Máquina de Estados de Ciclo de Vida com Enum: enums avançados com métodos abstratos para validar transições de status legais.
- Padrão Strategy Embutido em Enum: cálculo de frete logístico especializado sem encadeamentos de
if/else.
💻 Exemplo 01 — Sistema Completo de Checkout e Máquina de Estados
package com.empresa.composicao;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Sistema corporativo de pedidos com composição, agregacão e enums comportamentais.
*/
public class SistemaCheckoutComposicao {
public enum ModalidadeFrete {
RETIRADA_LOJA {
@Override
public BigDecimal calcular(BigDecimal pesoTotalKg) { return BigDecimal.ZERO; }
},
PAC_CORREIOS {
@Override
public BigDecimal calcular(BigDecimal pesoTotalKg) {
return new BigDecimal("22.50").add(pesoTotalKg.multiply(new BigDecimal("2.10")));
}
},
SEDEX_EXPRESSO {
@Override
public BigDecimal calcular(BigDecimal pesoTotalKg) {
return new BigDecimal("45.00").add(pesoTotalKg.multiply(new BigDecimal("4.80")));
}
};
public abstract BigDecimal calcular(BigDecimal pesoTotalKg);
}
public enum StatusPedido {
CRIADO {
@Override
public boolean podeAvancar(StatusPedido proximo) { return proximo == PAGO || proximo == CANCELADO; }
},
PAGO {
@Override
public boolean podeAvancar(StatusPedido proximo) { return proximo == EM_TRANSPORTE || proximo == CANCELADO; }
},
EM_TRANSPORTE {
@Override
public boolean podeAvancar(StatusPedido proximo) { return proximo == ENTREGUE; }
},
ENTREGUE {
@Override
public boolean podeAvancar(StatusPedido proximo) { return false; }
},
CANCELADO {
@Override
public boolean podeAvancar(StatusPedido proximo) { return false; }
};
public abstract boolean podeAvancar(StatusPedido proximo);
}
public record Cliente(String id, String nome, String email) {}
public record Produto(String sku, String nome, BigDecimal precoUnitario, BigDecimal pesoKg) {}
public static class ItemPedido {
private final Produto produto;
private final int quantidade;
private final BigDecimal precoPraticado;
public ItemPedido(Produto produto, int quantidade) {
this.produto = produto;
this.quantidade = quantidade;
this.precoPraticado = produto.precoUnitario();
}
public BigDecimal getSubtotal() {
return precoPraticado.multiply(new BigDecimal(quantidade));
}
public BigDecimal getPesoTotal() {
return produto.pesoKg().multiply(new BigDecimal(quantidade));
}
public Produto getProduto() { return produto; }
public int getQuantidade() { return quantidade; }
}
public static class Pedido {
private final String numeroProtocolo;
private final LocalDateTime dataCriacao;
private final Cliente cliente; // Agregação (Cliente existe de forma independente)
private final List<ItemPedido> itens = new ArrayList<>(); // Composição Forte (Itens pertencem a este pedido)
private ModalidadeFrete freteEscolhido = ModalidadeFrete.RETIRADA_LOJA;
private StatusPedido status = StatusPedido.CRIADO;
public Pedido(String numeroProtocolo, Cliente cliente) {
this.numeroProtocolo = numeroProtocolo;
this.cliente = cliente;
this.dataCriacao = LocalDateTime.now();
}
public void adicionarItem(Produto produto, int quantidade) {
if (this.status != StatusPedido.CRIADO) {
throw new IllegalStateException("Pedido bloqueado para adição de itens (Status: " + status + ")");
}
itens.add(new ItemPedido(produto, quantidade));
}
public void definirFrete(ModalidadeFrete frete) {
this.freteEscolhido = frete;
}
public void mudarStatus(StatusPedido novoStatus) {
if (!this.status.podeAvancar(novoStatus)) {
throw new IllegalStateException(String.format("Transição ilegal de [%s] para [%s]", status, novoStatus));
}
System.out.printf(" [MUDANÇA DE ESTADO] Pedido %s: %s ➡️ %s%n", numeroProtocolo, status, novoStatus);
this.status = novoStatus;
}
public BigDecimal calcularSubtotalItens() {
return itens.stream().map(ItemPedido::getSubtotal).reduce(BigDecimal.ZERO, BigDecimal::add);
}
public BigDecimal calcularPesoTotal() {
return itens.stream().map(ItemPedido::getPesoTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
}
public BigDecimal calcularValorTotal() {
BigDecimal subtotal = calcularSubtotalItens();
BigDecimal custoFrete = freteEscolhido.calcular(calcularPesoTotal());
return subtotal.add(custoFrete).setScale(2, RoundingMode.HALF_UP);
}
public void imprimirSumario() {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
System.out.println("================================================================================");
System.out.printf(" PEDIDO DE VENDA: %s%n", numeroProtocolo);
System.out.println("================================================================================");
System.out.printf("Data: %s | Status: [%s]%n", dataCriacao.format(fmt), status);
System.out.printf("Cliente: %s (%s)%n", cliente.nome(), cliente.email());
System.out.println("--------------------------------------------------------------------------------");
System.out.printf("%-12s | %-30s | %4s | %-10s | %-10s%n", "SKU", "PRODUTO", "QTD", "UNITÁRIO", "SUBTOTAL");
System.out.println("-------------+--------------------------------+------+------------+-------------");
for (ItemPedido item : itens) {
System.out.printf("%-12s | %-30s | %4d | R$ %8.2f | R$ %8.2f%n",
item.getProduto().sku(), item.getProduto().nome(),
item.getQuantidade(), item.getProduto().precoUnitario(), item.getSubtotal());
}
BigDecimal custoFrete = freteEscolhido.calcular(calcularPesoTotal());
System.out.println("--------------------------------------------------------------------------------");
System.out.printf("Subtotal dos Itens : R$ %10.2f%n", calcularSubtotalItens());
System.out.printf("Frete (%-14s): R$ %10.2f (Peso: %.2f kg)%n", freteEscolhido, custoFrete, calcularPesoTotal());
System.out.printf("TOTAL DO PEDIDO : R$ %10.2f%n", calcularValorTotal());
System.out.println("================================================================================");
}
public List<ItemPedido> getItens() { return Collections.unmodifiableList(itens); }
}
public static void main(String[] args) {
Cliente cli = new Cliente("CLI-890", "Fernando Silveira", "fernando@empresa.com.br");
Pedido pedido = new Pedido("PED-2026-9041", cli);
pedido.adicionarItem(new Produto("MON-DELL-27", "Monitor Dell 27 4K", new BigDecimal("2499.00"), new BigDecimal("6.50")), 2);
pedido.adicionarItem(new Produto("KB-MECH-RGB", "Teclado Mecânico Keychron", new BigDecimal("650.00"), new BigDecimal("1.20")), 1);
pedido.definirFrete(ModalidadeFrete.SEDEX_EXPRESSO);
pedido.imprimirSumario();
// Transições de Estado
System.out.println("\n--- Fluxo do Ciclo de Vida ---");
pedido.mudarStatus(StatusPedido.PAGO);
pedido.mudarStatus(StatusPedido.EM_TRANSPORTE);
pedido.mudarStatus(StatusPedido.ENTREGUE);
// Teste de Transição Inválida
try {
System.out.println("\n--- Teste de Violação: Tentar Cancelar Pedido Já Entregue ---");
pedido.mudarStatus(StatusPedido.CANCELADO);
} catch (IllegalStateException e) {
System.out.println("🛡️ Interceptação de Regra de Negócio: " + e.getMessage());
}
}
}
📋 Resultado Esperado no Console
================================================================================
PEDIDO DE VENDA: PED-2026-9041
================================================================================
Data: 28/09/2026 15:30 | Status: [CRIADO]
Cliente: Fernando Silveira (fernando@empresa.com.br)
--------------------------------------------------------------------------------
SKU | PRODUTO | QTD | UNITÁRIO | SUBTOTAL
-------------+--------------------------------+------+------------+-------------
MON-DELL-27 | Monitor Dell 27 4K | 2 | R$ 2499.00 | R$ 4998.00
KB-MECH-RGB | Teclado Mecânico Keychron | 1 | R$ 650.00 | R$ 650.00
--------------------------------------------------------------------------------
Subtotal dos Itens : R$ 5648.00
Frete (SEDEX_EXPRESSO): R$ 113.16 (Peso: 14.20 kg)
TOTAL DO PEDIDO : R$ 5761.16
================================================================================
--- Fluxo do Ciclo de Vida ---
[MUDANÇA DE ESTADO] Pedido PED-2026-9041: CRIADO ➡️ PAGO
[MUDANÇA DE ESTADO] Pedido PED-2026-9041: PAGO ➡️ EM_TRANSPORTE
[MUDANÇA DE ESTADO] Pedido PED-2026-9041: EM_TRANSPORTE ➡️ ENTREGUE
--- Teste de Violação: Tentar Cancelar Pedido Já Entregue ---
🛡️ Interceptação de Regra de Negócio: Transição ilegal de [ENTREGUE] para [CANCELADO]