omastraa

Don’t use if-else blocks anymore! Use Strategy and Factory Pattern Together

As we move forward in a project, lost in if-else blocks, struggling with complex conditions and repetitive code, we look for a solution. But why should we be stuck in if-else blocks? In this article, let’s discover the way to get rid of if-else confusion together with Strategy and Factory patterns.

Problem: If-Else Confusion

Let’s say you are developing an e-commerce application and you need to support different payment methods like credit card, debit card and cryptocurrency. You start with if-else blocks to process payments:
 

public class PaymentService {    public void processPayment(String paymentType) {        if (paymentType.equals("CREDIT_CARD")) {            System.out.println("Processing credit card payment...");        } else if (paymentType.equals("DEBIT_CARD")) {            System.out.println("Processing debit card payment...");        } else if (paymentType.equals("CRYPTO")) {            System.out.println("Processing crypto payment...");        } else {            throw new IllegalArgumentException("Invalid payment type");        }    } }

 

While it may seem simple at first, as payment methods increase, so will the if-else complexity. A new payment method means adding a new condition. The result is a pile of code that is difficult to manage. And this method is contrary to the Open-Closed Principle.

But, we can use the both Strategy and Factory patterns to solve this problem.

First, let’s create an enum:
 

public enum PaymentType {    CREDIT_CARD,    DEBIT_CARD,    CRYPTO }

 

Solution: Cleaning with Strategy Pattern

public interface PaymentStrategy {    void pay(PaymentRequest request); } public class CreditCardPayment implements PaymentStrategy {    @Override    public void pay(PaymentRequest request) {        System.out.println("Processing $type payment".replace("$type", String.valueOf(request.getPaymentType())));    } } public class DebitCardPayment implements PaymentStrategy {    @Override    public void pay(PaymentRequest request) {        System.out.println("Processing $type payment".replace("$type", String.valueOf(request.getPaymentType())));    } } public class CryptoPayment implements PaymentStrategy {    @Override    public void pay(PaymentRequest request) {        System.out.println("Processing $type payment".replace("$type", String.valueOf(request.getPaymentType())));    } }

 

At this stage, a separate strategy for each payment method is implemented from a common interface. Now, with Factory Pattern, we will decide which strategy to choose.

Step 2: Choosing Strategy with Factory Pattern

In this step, we can make the Factory Pattern cleaner and optimized with EnumMap.
 

public class PaymentFactory {    private static final Map<PaymentType, PaymentStrategy> strategies = new EnumMap<>(PaymentType.class);    static {        strategies.put(PaymentType.CREDIT_CARD, new CreditCardPayment());        strategies.put(PaymentType.DEBIT_CARD, new DebitCardPayment());        strategies.put(PaymentType.CRYPTO, new CryptoPayment());    }    public static PaymentStrategy getPaymentStrategy(PaymentType paymentType) {        PaymentStrategy strategy = strategies.get(paymentType);        if (Objects.isNull(strategy))            throw new IllegalArgumentException("Strategy not found");        return strategy;    } }

 

Last Step: Service Class Reorganization

Now, let’s use what we have done.
 

public class PaymentService {    public void processPayment(PaymentRequest request) {        // Don't forget to check objects if null!        if (Objects.isNull(request) || Objects.isNull(request.getPaymentType())            throw new IllegalArgumentException("Request can not be null!");        PaymentStrategy strategy = PaymentFactory.getPaymentStrategy(request.getPaymentType());        strategy.pay(request);    } }

 

As it is, we don’t need any if-else blocks for payment processing. Thanks to Strategy and Factory Patterns, our code is cleaner, modular and extensible.

Why Should We Use This Patterns?

1. Extensibility: Adding a new payment method only requires a new class and a few lines of code.
2. Readability: By using strategies and factory instead of if-else blocks, you make your code more understandable and manageable.
3. Maintainability: With the strategy and factory pattern, changes to the code can be made without affecting other pieces of code.

Conclusion: From Confusion to Clarity

If you are working on a growing project, you shouldn’t use if-else blocks. Strategy and Factory patterns are perfect solutions to make your code cleaner, modular and maintainable.

As you can see in this article, using design patterns instead of if-else blocks to manage payment transactions makes the project more developable and improves the readability of the code. Try these patterns in your next project instead of using if-else blocks.

...

Thank you for reading my article! If you have any questions, feedback or thoughts you would like to share, I would love to hear them in the comments.

You can follow me on dev.to for more information on this topic and my other posts.

Thank you!👨‍💻🚀