Lesson 1
Strategy Pattern
Introduction to the Strategy Pattern

Welcome to the lesson on the Strategy pattern, a key concept in Behavioral Design Patterns. In this unit, we explore behavioral design patterns that enable complex interactions between objects. Behavioral patterns focus on how objects interact and communicate with each other to distribute responsibilities. We will begin with the Strategy pattern, which helps object-oriented systems choose the appropriate algorithm at runtime.

What You Will Learn

By the end of this lesson, you will:

  • Understand the concept of the Strategy pattern.
  • Learn how to implement and use it in Java.
  • Recognize the benefits of using the Strategy pattern in software design.
Understanding and Implementing the Strategy Pattern

The Strategy pattern is a behavioral design pattern that allows you to define a family of algorithms, encapsulate each one in a separate class, and make them interchangeable within the context object. This encapsulation allows you to select and change the algorithm at runtime, providing flexibility and decoupling the client's code from the specific implementations of the algorithms. The pattern is particularly useful when you have multiple ways to perform a task and want to enable the dynamic selection of the algorithm based on the application's current state or user input.

Let's look at a practical example involving different payment methods for a shopping cart. Here, the Strategy pattern allows us to switch between various payment methods—such as credit card and PayPal—dynamically during checkout. By defining a common interface for payment strategies and creating separate classes for each method, the ShoppingCart can easily switch between them at runtime, enhancing flexibility and maintainability.

Step 1: Define the Strategy Interface

First, we create an interface representing our payment method.

Java
1public interface PaymentStrategy { 2 void pay(int amount); 3}

Here, we define a PaymentStrategy interface with a single method, pay(int amount). Any class implementing this interface will need to provide an implementation for the pay method, allowing that class's own payment logic to be used.

Step 2: Implement Concrete Strategy Classes

Now, let's create two concrete strategies: one for payments via credit card and another for PayPal.

Java
1public class CreditCardStrategy implements PaymentStrategy { 2 private String cardNumber; 3 4 public CreditCardStrategy(String cardNumber) { 5 this.cardNumber = cardNumber; 6 } 7 8 @Override 9 public void pay(int amount) { 10 System.out.println("Paid " + amount + " using Credit Card: " + cardNumber); 11 } 12}

The CreditCardStrategy class implements the PaymentStrategy interface and provides a specific implementation for the pay method. This class uses a credit card number passed to its constructor to process the payment.

Java
1public class PayPalStrategy implements PaymentStrategy { 2 private String email; 3 4 public PayPalStrategy(String email) { 5 this.email = email; 6 } 7 8 @Override 9 public void pay(int amount) { 10 System.out.println("Paid " + amount + " using PayPal: " + email); 11 } 12}

The PayPalStrategy class also implements the PaymentStrategy interface but processes the payment using an email address. This example demonstrates how different concrete strategies can provide their own specific implementations of the same method.

Step 3: Create the Context Class
Java
1public class ShoppingCart { 2 private PaymentStrategy strategy; 3 4 public void setPaymentStrategy(PaymentStrategy strategy) { 5 this.strategy = strategy; 6 } 7 8 public void checkout(int amount) { 9 if (strategy != null) { 10 strategy.pay(amount); 11 } else { 12 System.out.println("No payment strategy set."); 13 } 14 } 15}

The ShoppingCart class uses a PaymentStrategy reference to process payments. By using the setPaymentStrategy method, the payment strategy can be changed at runtime. The checkout method ensures that a payment strategy is set before attempting to process a payment, providing a safety check before calling the pay method.

Step 4: Using the Strategy Pattern in Practice

Finally, let's see how to use these classes together.

Java
1public class Main { 2 public static void main(String[] args) { 3 ShoppingCart cart = new ShoppingCart(); 4 5 CreditCardStrategy creditCard = new CreditCardStrategy("1234-5678-9876-5432"); 6 PayPalStrategy payPal = new PayPalStrategy("user@example.com"); 7 8 cart.setPaymentStrategy(creditCard); 9 cart.checkout(100); 10 11 cart.setPaymentStrategy(payPal); 12 cart.checkout(200); 13 } 14}

In the Main class, we create a ShoppingCart instance and two payment strategies: CreditCardStrategy and PayPalStrategy. By changing the payment strategy at runtime with the setPaymentStrategy method, we demonstrate the flexibility of the Strategy pattern. We process two payments, first using the credit card strategy and then using the PayPal strategy.

Why the Strategy Pattern is Important

The Strategy pattern provides flexibility and reusability in your code:

  1. Flexibility: It allows the algorithm to be selected at runtime, enabling dynamic behavior changes.

  2. Reusability: Individual algorithms are encapsulated in separate classes, making them reusable across different contexts.

This pattern follows the Open/Closed Principle, as new strategies can be introduced without modifying the existing code.

Now that you understand the Strategy pattern, it's time to dive into the practice section and apply what you've learned. This hands-on approach will reinforce your understanding and help you see how powerful and flexible the Strategy pattern can be. Let's get started!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.