Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

[Projeto] Refatoracao de codigo

Fiz uma refatoração no código do instrutor no desafio Sistema de pagamentos para utilizar o design pattern Strategy & Simple Factory, parando para analisar que, em uma startup ou empresa real, esses métodos de pagamento poderiam crescer e surgir novas formas de se efetuar o pagamento. Desta forma, o client tem de estar ciente das estratégias para saber qual se aplica a ele e, também, desta forma, a aplicação fica mais escalável e propícia a testes, já que posso gerenciar manualmente cada uma sem necessitar das instâncias diretamente, o que deixaria o código fortemente acoplado e dificil para manutenção.

Class Main

package org.maelys.strategy;

import org.maelys.strategy.factory.PaymentFactory;
import org.maelys.strategy.service.Payment;

import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {

        Payment payment = new Payment();
        var type = PaymentFactory.getPayment("CreditCard");

        payment.setPaymentInterface(type);
        BigDecimal result = payment.calculate(new BigDecimal("100000"));
        System.out.println(result);
        payment.confirmPayment(result);
    }
}

Class PaymentFactory

package org.maelys.strategy.factory;

import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.maelys.strategy.service.PaymentInterface;
import org.maelys.strategy.service.internal.Boleto;
import org.maelys.strategy.service.internal.CreditCard;
import org.maelys.strategy.service.internal.Pix;

public class PaymentFactory {
    @Contract("_ -> new")
    public static @NotNull PaymentInterface getPayment(@NotNull String paymentType) {
        switch (paymentType) {
            case "Boleto" -> {
                return new Boleto();
            }
            case "Pix" -> {
                return new Pix();
            }
            case "CreditCard" -> {
                return new CreditCard();
            }
            default -> throw new IllegalStateException("Unexpected value: " + paymentType);
        }
    }
}

Class Payment

package org.maelys.strategy.service;

import java.math.BigDecimal;
import java.text.DecimalFormat;

@lombok.AllArgsConstructor(access = lombok.AccessLevel.PRIVATE)
@lombok.NoArgsConstructor(access = lombok.AccessLevel.PUBLIC)
public class Payment {
    @lombok.Setter(value = lombok.AccessLevel.PUBLIC)
    private PaymentInterface paymentInterface;

    public void confirmPayment(BigDecimal amount) {
        DecimalFormat formatter = new DecimalFormat("R$ #,###0.00");
        System.out.printf("Payment of %s confirmed%n", formatter.format(amount));
    }

    public BigDecimal calculate(BigDecimal amount) {
        return paymentInterface.calculate(amount);
    }
}

Interface PaymentInterface

package org.maelys.strategy.service;

import java.math.BigDecimal;

@FunctionalInterface
public interface PaymentInterface {
    BigDecimal calculate(BigDecimal amount);
}

Class Pix

package org.maelys.strategy.service.internal;

import org.maelys.strategy.service.PaymentInterface;

import java.math.BigDecimal;

public class Pix implements PaymentInterface {
    @Override
    public BigDecimal calculate(BigDecimal amount) {
        return BigDecimal.ZERO;
    }
}

Class CreditCard

package org.maelys.strategy.service.internal;

import org.jetbrains.annotations.NotNull;
import org.maelys.strategy.service.PaymentInterface;

import java.math.BigDecimal;

public class CreditCard implements PaymentInterface {
    @Override
    public BigDecimal calculate(@NotNull BigDecimal amount) {
        return amount.multiply(new BigDecimal("0.03"));
    }
}

Class Boleto

package org.maelys.strategy.service.internal;

import org.jetbrains.annotations.NotNull;
import org.maelys.strategy.service.PaymentInterface;

import java.math.BigDecimal;

public class Boleto implements PaymentInterface {
    @Override
    public BigDecimal calculate(@NotNull BigDecimal amount) {
        return amount.multiply(new BigDecimal("0.01"));
    }
}
4 respostas
solução!

Oi, Rick! Como vai?

Sua implementação do Strategy junto com Simple Factory deixou o sistema bem preparado para expansão e manutenção. Esse tipo de arquitetura realmente ajuda quando novas formas de pagamento surgem, pois evita acoplamento e facilita testes.

Uma dica interessante para o futuro é implementar um map de estratégias em vez de usar switch, permitindo adicionar novas opções sem alterar o código existente:


Map<String, PaymentInterface> strategies = Map.of(
    "Boleto", new Boleto(),
    "Pix", new Pix(),
    "CreditCard", new CreditCard()
);

PaymentInterface payment = strategies.get("Pix");
BigDecimal result = payment.calculate(new BigDecimal("100"));
System.out.println(result);

Esse código cria um mapeamento de formas de pagamento, tornando fácil buscar a estratégia desejada.

Alura Conte com o apoio da comunidade Alura na sua jornada. Abraços e bons estudos!

Muito obrigado pela dica, irei guardar comigo.

Nao sei se esta correto, fiz em outro projeto essa aplicacao do Map, eu criei uma variavel do tipo Map<String, Supplier<FreightInterface>> passei os valores e refiz o metodo de get para obter a estrategia usando Map agora, ficou assim:
public class FreightFactory {

package org.maelys.practice.factory;

import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import org.maelys.practice.service.internal.PatrusTransportes;
import org.maelys.practice.service.internal.WithdrawalStore;
import org.maelys.practice.service.internal.Correios;
import org.maelys.practice.service.FreightInterface;

import java.util.Map;
import java.util.function.Supplier;

public class FreightFactory {

    private static final Map<String, Supplier<FreightInterface>> FREIGHT_MAP =
            Map.of(
            "Correios", Correios::new,
            "PatrusTransportes", PatrusTransportes::new,
            "WithdrawalStore", WithdrawalStore::new
    );

    @Contract("_ -> new")
    public static @NotNull FreightInterface getFreight(@NotNull String freightType) {
        Supplier<FreightInterface> freight = FREIGHT_MAP.get(freightType);
        if (freight == null) throw new IllegalArgumentException("Freight type not found");
        return freight.get();
    }
}

Oi, Rick!

Sua ideia de aplicar o Map junto com Supplier para a factory ficou ótima, porque elimina a necessidade de alterar o código da classe sempre que for adicionada uma nova estratégia. Isso garante um código mais aberto para extensão e fechado para modificação, alinhado ao princípio OCP do SOLID.

No seu caso, a implementação está correta. Apenas sugiro ajustar o retorno do método para deixar o código mais limpo e claro, evitando null. Resolva fazendo o seguinte:


public static @NotNull FreightInterface getFreight(@NotNull String freightType) {
    return FREIGHT_MAP
        .getOrDefault(freightType, () -> { 
            throw new IllegalArgumentException("Freight type not found: " + freightType); 
        })
        .get();
}

Assim, você evita a verificação manual de null e concentra a responsabilidade dentro do próprio Map. Além disso, a mensagem de erro fica mais informativa.

Fico à disposição.