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!👨💻🚀