Strategy em Java para desacoplar o cálculo de frete
Autor: Emanuel Antônio Lima Rocha 1 Base fundamental Introdução O Strategy é um padrão comportamental que reúne maneiras alternativas de executar uma operação sob um contrato comum, permitindo escolher a implementação usada pelo contexto. No catálogo de Gamma e colaboradores, ele pertence à categoria dos padrões comportamentais [1]. A separação entre contexto, interface e estratégias concretas permite variar o algoritmo por composição [2]. O problema de design Considere um checkout legado que calcula frete econômico e expresso dentro do mesmo método. Cada modalidade acrescenta condições e regras nesse ponto central. No estudo de caso deste artigo, o gargalo é a dificuldade de manutenção: alterar uma tarifa exige editar código que também trata outras modalidades. Trata-se de um cenário didático verossímil, e não de um relato de uma empresa ou de resultados medidos. Como o padrão funciona O contexto mantém uma referência ao contrato da estratégia e delega a operação à implementação recebida. O cliente escolhe a estratégia; o contexto não precisa conhecer sua classe concreta. A analogia é a escolha de uma modalidade de entrega: o checkout pede uma cotação, enquanto cada modalidade decide como calculá-la [2]. No exemplo, CalculadoraFrete é o contexto, FreteStrategy é o contrato e FreteEconomico e FreteExpresso são as alternativas. Main faz a composição dos objetos. 2 Desenvolvimento Estudo de caso de mercado Uma loja virtual precisa acrescentar modalidades de frete sem concentrar todas as fórmulas no checkout. A proposta é extrair cada regra para uma estratégia. Para tornar o exemplo reproduzível, as tarifas são fictícias: econômico custa R$ 8,00 mais R$ 2,50 por quilograma; expresso custa R$ 15,00 mais R$ 4,00 por quilograma. Um pedido de 3 kg resulta em R$ 15,50 ou R$ 27,00, respectivamente. Esses valores são premissas do exemplo, não preços de transportadoras. Diagrama de classes A Figura 1 representa as classes do código. A seta com ponta vazada indica implementação da interface; a seta de CalculadoraFrete indica navegação para uma estratégia. Main cria Pedido e CalculadoraFrete e seleciona as implementações. Pedido é parâmetro de calcular nas três classes e na interface; essas dependências de parâmetro estão descritas aqui para manter a figura legível. O símbolo + indica acesso público, - privado e ~ acesso de pacote. Figura 1 - Estrutura UML do Strategy aplicado ao frete. Fonte: elaboração própria, com base na estrutura do padrão [2]. Arquitetura de software Onde o padrão se encaixa Na arquitetura proposta, o cliente chama a API de checkout por HTTPS. A API valida o pedido e a modalidade, seleciona uma estratégia e cria uma CalculadoraFrete para aquela solicitação. A calculadora delega o cálculo à regra escolhida. A API acessa o banco para consultar o pedido e registrar a cotação. Apenas uma estratégia é executada por cotação; as duas setas representam alternativas, não execução paralela. Figura 2 - Arquitetura de alto nível do checkout e localização do Strategy. Fonte: elaboração própria. Escopo da implementação O código abaixo implementa o núcleo de cálculo e um cliente de console. Main exerce o papel de seleção que a API teria. HTTP e persistência contextualizam o sistema na Figura 2 e não são implementados neste exemplo. As estratégias usam regras locais; não há chamada a transportadoras externas. CEP, peso cubado, restrições de atendimento e prazo precisariam ser modelados antes de uma adoção real. Essa delimitação permite avaliar o padrão sem simular uma integração que o código não executa. Implementação em Java Arquivo Main.java - Java 17 ou superior. BigDecimal evita a representação binária de double; as constantes são construídas a partir de strings. O arredondamento HALF_UP com duas casas é uma decisão explícita deste exemplo [3]. import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Map; import java.util.Objects; public class Main { public static void main(String[] args) { var estrategias = Map.of( "economico", new FreteEconomico(), "expresso", new FreteExpresso()); String modalidade = args.length == 0 ? "economico" : args[0]; FreteStrategy estrategia = estrategias.get(modalidade); if (estrategia == null) { throw new IllegalArgumentException("Modalidade inválida"); } var pedido = new Pedido(new BigDecimal("3.00")); var calculadora = new CalculadoraFrete(estrategia); System.out.println("Frete: R$ " + calculadora.calcular(pedido)); } } record Pedido(BigDecimal pesoKg) { Pedido { Objects.requireNonNull(pesoKg, "Peso obrigatório"); if (pesoKg.signum() <= 0) { throw new IllegalArgumentException("Peso deve ser positivo"); } } } interface FreteStrategy { BigDecimal calcular(Pedido pedido); } Estratégias e contexto final class FreteEconomico implements FreteStrategy { @override public BigDecimal calcular(Pedido pedido) { return new BigDecimal("8.00") .add(pedido.pesoKg().multiply(new BigDecimal("2.50"))) .setScale(2, RoundingMode.HALF_UP); } } final class FreteExpresso implements FreteStrategy { @override public BigDecimal calcular(Pedido pedido) { return new BigDecimal("15.00") .add(pedido.pesoKg().multiply(new BigDecimal("4.00"))) .setScale(2, RoundingMode.HALF_UP); } } final class CalculadoraFrete { private final FreteStrategy estrategia; CalculadoraFrete(FreteStrategy estrategia) { this.estrategia = Objects.requireNonNull(estrategia); } BigDecimal calcular(Pedido pedido) { return estrategia.calcular(Objects.requireNonNull(pedido)); } } Execução e análise da solução Como executar Execute java Main.java economico para obter Frete: R$ 15.50; execute java Main.java expresso para obter Frete: R$ 27.00. Também é possível compilar com javac Main.java e executar java Main economico. O ponto decimal vem da representação textual de BigDecimal; uma interface brasileira deve formatar a moeda para exibição. Correspondência entre código e diagrama Pedido valida um peso obrigatório e positivo. FreteStrategy define calcular(Pedido). As duas classes concretas implementam esse método. CalculadoraFrete guarda uma referência final à interface e delega a chamada. Main usa um registro de estratégias por nome e rejeita modalidades desconhecidas. A escolha muda entre solicitações pela construção de outro contexto, sem um setter compartilhado. Nenhuma classe mostrada na UML exige um framework. Vantagens observadas no desenho Cada fórmula fica em uma classe específica, o que permite revisar e testar a regra isoladamente. O contexto permanece igual quando uma nova implementação é adicionada. A composição evita herdar uma calculadora para cada modalidade [2]. Neste desenho, adicionar uma modalidade exige criar a estratégia e atualizar o registro no cliente; a configuração ainda precisa mudar, embora CalculadoraFrete permaneça intacta. Custos e limites O projeto passa a ter mais classes e um ponto de seleção que precisa conhecer as modalidades. Regras muito simples e estáveis podem não justificar essa estrutura [2]. No exemplo, a validação de peso é comum, mas cada estratégia é responsável por preservar o contrato do resultado. Uma evolução com APIs externas exigiria definir falhas, timeout e validade da cotação. Strategy organiza algoritmos; por si só não cria filas, tolerância a falhas ou escalabilidade. Não foram usados benchmarks para sustentar ganhos de latência. Verificações relevantes A validação deve cobrir as duas tarifas para 3 kg, peso zero ou negativo, peso nulo, modalidade desconhecida e pesos fracionários. O caso de 1,333 kg econômico deve resultar em 11,33 após arredondamento. Essas verificações protegem regras de negócio e o contrato comum, em vez de apenas conferir a existência das classes. 3 Conclusão Resumo A solução retira as fórmulas de frete do ponto central do checkout e as coloca em implementações substituíveis. O valor arquitetural está na separação de responsabilidades e na possibilidade de acrescentar regras preservando o contexto. O estudo de caso mostra como aplicar o Strategy a um problema de manutenção, com escopo e limitações explícitos. Visão pessoal Minha leitura deste estudo de caso é que o aspecto mais útil do Strategy está em identificar o comportamento que varia antes de criar novas classes. Considero a solução adequada quando modalidades evoluem de forma independente. Para poucas regras estáveis, eu avaliaria uma implementação mais simples. Aprender o padrão exige entender a delegação e os seus custos, além de reconhecer a estrutura no diagrama. Pergunta para os leitores No seu projeto, qual conjunto de condicionais poderia ser transformado em estratégias, e o que justificaria essa mudança? Referências [1] GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; VLISSIDES, John. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994. Catálogo bibliográfico da editora, com Strategy na categoria comportamental. Disponível em: https://www.informit.com/store/design-patterns-elements-of-reusable-object-oriented-9780321770462. Acesso em: 13 set. 2026. [2] REFACTORING GURU. Strategy. Estrutura, aplicação e trade-offs do padrão. Disponível em: https://refactoring.guru/design-patterns/strategy. Acesso em: 13 set. 2026. [3] ORACLE. Java Platform Standard Edition 21. BigDecimal. Documentação oficial da API. Disponível em: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/math/BigDecimal.html. Acesso em: 13 set. 2026. Top comments (0)
Comments
No comments yet. Start the discussion.