The strategy design pattern is a behavioral pattern in which we have multiple algorithms/strategies for achieving a task and which algorithm/strategy to use is left for the client to choose. The various algorithm options are encapsulated in individual classes.
In this tutorial, we’ll learn to implement the strategy design pattern in Java.
Let’s first start by looking at the UML representation of the strategy design pattern:
Here, we have:
One popular example of the Strategy pattern in JDK is the usage of java.util.Comparator in the Collections.sort() method. We can think of Collections.sort() method to be the context and the java.util.Comparator instance that we pass-in as the strategy for sorting objects.
As we know, any shopping website offers multiple payment options. So, let’s use this example to implement the strategy pattern.
We’ll first define our PaymentStrategy interface:
public interface PaymentStrategy { void pay(Shopper shopper); }
Now, let’s define the two most common modes of payments, Cash on Delivery and Card payment, as the two concrete strategy classes:
public class CashOnDeliveryStrategy implements PaymentStrategy { @Override public void pay(Shopper shopper) { double amount = shopper.getShoppingCart().getTotal(); System.out.println(shopper.getName() + " selected Cash On Delivery for Rs." + amount ); } } public class CardPaymentStrategy implements PaymentStrategy { @Override public void pay(Shopper shopper) { CardDetails cardDetails = shopper.getCardDetails(); double amount = shopper.getShoppingCart().getTotal(); completePayment(cardDetails, amount); System.out.println("Credit/Debit card Payment of Rs. " + amount + " made by " + shopper.getName()); } private void completePayment(CardDetails cardDetails, double amount) { ... } }
Having defined our strategy classes, let’s now define a PaymentContext class:
public class PaymentContext { private PaymentStrategy strategy; public PaymentContext(PaymentStratgey strategy) { this.strategy = strategy; } public void makePayment(Shopper shopper) { this.strategy.pay(shopper); } }
Also, our Shopper class would look similar to:
public class Shopper { private String name; private CardDetails cardDetails; private ShoppingCart shoppingCart; //suitable constructor , getters and setters public void addItemToCart(Item item) { this.shoppingCart.add(item); } public void payUsingCOD() { PaymentContext pymtContext = new PaymentContext(new CashOnDeliveryStrategy()); pymtContext.makePayment(this); } public void payUsingCard() { PaymentContext pymtContext = new PaymentContext(new CardPaymentStrategy()); pymtContext.makePayment(this); } }
A Shopper in our system can pay using one of the available strategies for his/her purchases. For our example, our PaymentContext class accepts the selected payment strategy and then invokes the pay() method for that strategy.
Both strategy and state design patterns are interface based patterns and might look similar but have some important differences:
Please feel free to further explore the State Design Pattern.
With this quick tutorial, we now know how to implement a Strategy design pattern.
It’s one of the most commonly used design patterns and obeys the Open/Closed principle. So, to add a new strategy, we can simply create an additional strategy class. However, please note that we’ll have to update the client code as well here as the client chooses the strategy to be invoked.