Hello! Welcome to the first lesson in our new module on behavioral patterns that deal with responsibility and algorithms.
In the previous module, we focused on how objects communicate. We explored patterns like Observer, Mediator, and Command, which all aim to decouple senders and receivers. Now, we shift our focus to patterns that manage how an object performs its work, specifically when it has multiple ways—or algorithms—to do so.
Today's lesson addresses the learning outcome: Apply the Strategy pattern to define a family of interchangeable algorithms. We'll explore how this elegant pattern allows you to encapsulate different algorithms into separate classes, making them swappable at runtime. This is a fundamental pattern for creating flexible and maintainable systems, and it's highly relevant for low-level design interviews.
The Problem: When One Task Has Many Methods
Imagine you're building a service that needs to perform a specific action, but the exact implementation of that action can vary. A common example is a payment service. A customer might choose to pay by credit card, PayPal, or a bank transfer.
A straightforward, but problematic, way to code this is with a large if-else or switch statement inside a single method. Let's see what that looks like and why it's a design issue.
The Strategy Pattern Explained and Implemented in Java | Behavioral Design Patterns | Geekific
The video 'The Strategy Pattern Explained and Implemented in Java' from Geekific provides a clear introduction to this problem. Watch the first part to see how a simple payment service can quickly become difficult to manage as new payment methods are added.
Watch from the beginning until the timestamp 01:52. Pay close attention to the critique of using conditional logic for handling different payment methods.
As the video points out, this approach violates two key SOLID principles we've discussed:
- Single Responsibility Principle (SRP): The
PaymentServiceclass is now responsible for the logic of every single payment method. Its responsibilities will grow and become muddled as more methods are added. - Open/Closed Principle (OCP): To add a new payment method (e.g., Apple Pay), you must open the
PaymentServiceclass and modify itsprocessOrdermethod. The class is not closed for modification.
This design leads to code that is brittle, hard to test, and difficult to maintain.
The Solution: Encapsulating Algorithms with the Strategy Pattern
The Strategy pattern offers a clean solution. Its core idea is to take a group of related algorithms, encapsulate each one in its own separate class, and make them interchangeable. The class that uses the algorithm (the "Context") will only know about a common interface, not the specific implementations.
Let's continue with the same video to see how to apply this pattern to the payment service example.
The Strategy Pattern Explained and Implemented in Java | Behavioral Design Patterns | Geekific
This next segment from the Geekific video demonstrates how to refactor the payment service using the Strategy pattern.
Watch from 01:52 to 03:59. This section defines the pattern and walks through its implementation, creating a PaymentStrategy interface and concrete classes like PaymentByCreditCard.
The Components of the Strategy Pattern
The pattern has three main components. The UML diagram below shows their relationship, using a sorting algorithm example.

-
Strategy Interface (
SortingStrategy): This is the common interface for all supported algorithms. It declares the method that the Context will call to execute the algorithm (e.g.,sort()). In our payment example, this wasPaymentStrategy. -
Concrete Strategies (
BubbleSortStrategy,QuickSortStrategy): These are the classes that implement the Strategy interface. Each class provides a specific algorithm (e.g., bubble sort, quick sort, credit card payment, PayPal payment). -
Context (
SortingContext): This is the class that needs the algorithm. It maintains a reference to a Strategy object. The Context does not know the concrete type of the strategy; it only communicates with it through the Strategy interface. It provides a way for the client to set or change the strategy.
Test your understanding!
Imagine you are designing a delivery application. The app needs to calculate the delivery route for a driver. There are several ways to do this: the "fastest route" (minimizing time), the "shortest route" (minimizing distance), and a "scenic route" (avoiding highways).
How would you map this scenario to the components of the Strategy pattern?
- What is the Context?
- What is the Strategy interface and what method might it have?
- What are the Concrete Strategies?
Show answer
- Context: The
RoutePlannerorNavigationServiceclass that is responsible for calculating and displaying a route. - Strategy Interface:
RouteCalculationStrategy. It would have a method likecalculateRoute(start, end). - Concrete Strategies:
FastestRouteStrategy,ShortestRouteStrategy, andScenicRouteStrategy. Each would implement thecalculateRoutemethod with its own specific algorithm.
Applying the Strategy Pattern in LLD Interviews
Your ability to apply this pattern to common interview problems is key. Many LLD questions involve a core functionality that can be implemented in multiple ways, making them perfect candidates for the Strategy pattern. The "Design a Parking Lot" problem you mentioned is a classic example.
Let's explore how to use the Strategy pattern in that specific context.
Using Strategy Design Pattern to solve questions asked in ...
The article 'Using Strategy Design Pattern to solve questions asked in Low Level Design Interview Rounds' by Prashant Priyadarshi provides excellent, interview-focused examples. We'll focus on the parking lot and the conclusion.
Read the section '2. Parking Lot Design: Assign parking spots to a vehicle using different parking strategies' and the final 'Pros and Cons to consider before using strategy pattern'. This will show you exactly how to apply the pattern to a common LLD problem and how to reason about your choice.
As the article demonstrates, when designing a parking lot, the requirement might be to support different ways of assigning a parking spot:
- Find the spot nearest to the entrance (
NearestParkingStrategy). - Find a spot on the floor with the most free spaces to ensure even distribution (
MostFreeSpotsParkingStrategy). - Find the spot nearest to an elevator for accessibility.
By defining a ParkingStrategy interface and implementing each rule in its own class, you create a system that is flexible and extensible. If the interviewer adds a new requirement—"now support a strategy for VIPs that assigns them a reserved spot"—you can confidently say you'll just add a new VipParkingStrategy class without modifying any existing code. This demonstrates a strong grasp of the Open/Closed Principle.
Connecting to Your Experience: Strategy in Spring Boot
Given your background in Java Spring Boot, you'll find that modern frameworks make implementing design patterns even more seamless. Spring's Dependency Injection (DI) container is perfectly suited for managing strategies.
This next resource shows a practical implementation within a Spring Boot application.
Implementing the Strategy Pattern in Java Spring Boot
The article 'Implementing the Strategy Pattern in Java Spring Boot' by James Storr shows a very common and powerful way to implement this pattern using Spring's features.
Read the sections 'Strategy Interface', 'Concrete Strategies', and 'Context Class'. Pay special attention to how Spring's annotations (@Component, @Autowired) are used to automatically collect all strategy implementations into a Map.
The key technique here is elegant:
- Each
ConcreteStrategyclass is annotated with@Component("strategyName"), turning it into a Spring bean with a specific name (e.g., "rain", "snow"). - The
Contextclass has its constructor autowired with aMap<String, ActivityStrategy>. - Spring automatically finds all beans that implement
ActivityStrategyand injects them into the map, using the bean name as the map key.
The Context now acts as a registry. To execute a strategy, it simply looks up the required implementation from the map using a key (like weatherCondition). This completely decouples the context from knowing about the concrete strategies and eliminates the need for a manual factory class.
Conclusion
Today we've delved into the Strategy pattern, a cornerstone of behavioral design. It's a powerful tool for building systems that are adaptable to changing requirements.
Key Takeaways:
- Purpose: The Strategy pattern allows you to define a family of algorithms, encapsulate each one, and make them interchangeable at runtime.
- Problem Solved: It helps you avoid large, hard-to-maintain conditional blocks (
if-else,switch) and promotes adherence to the Single Responsibility and Open/Closed principles. - Core Components: The pattern consists of a Context that uses an algorithm, a Strategy interface defining the algorithm's contract, and multiple Concrete Strategies that provide specific implementations.
- Interview Application: It's highly applicable in LLD scenarios where a core action can be performed in various ways, such as in a parking lot, search functionality, or payment processing.
- Modern Implementation: Frameworks like Spring Boot can simplify the pattern's implementation by using dependency injection to automatically register and provide strategies to the context.
In our next lesson, we will explore the State pattern. At first glance, its UML diagram looks almost identical to the Strategy pattern. However, its intent is quite different. We will focus on how the State pattern allows an object to alter its behavior when its internal state changes, and we'll contrast it directly with the Strategy pattern to clarify the distinction.