Strategy Pattern in a Spring Boot Project

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.

▶️ Subscribe to My YouTube Channel (176K+ subscribers): Java Guides on YouTube

▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube

In backend development, we often face scenarios where we need to execute different logic based on a type or condition — for example:

  • Different discount calculations based on customer type
  • Multiple payment methods
  • Various file parsers depending on the file format
  • Email vs. SMS vs. Push for notifications

You could easily go for if-else or switch statements to handle such logic. But as your project grows, that approach becomes hard to maintain and test.

That’s where the Strategy Pattern comes in.

In this article, we’ll cover:

  • What the Strategy Pattern is
  • When to use it in Spring Boot projects
  • A complete real-world example: calculating discounts based on customer type
  • Clean code with map-based Spring injection
  • Testing and extension tips

Let’s break it down, step-by-step.


What is the Strategy Pattern?

The Strategy Pattern is a behavioral design pattern that allows you to define a family of algorithms, encapsulate each one, and make them interchangeable at runtime.

Instead of hardcoding logic in if-else, you create a separate class for each strategy and choose the right one dynamically.

It’s especially helpful when you have different ways of doing the same kind of task — like calculating discounts.


Real-World Example: Dynamic Discount Strategy

Use Case

You're building an e-commerce platform where different types of customers get different discounts:

  • Regular: no discount
  • Premium: 10% discount
  • VIP: 20% discount

Your service should calculate the final price based on the customer type — and you want to avoid writing if-else blocks every time.

We’ll solve this using the Strategy Pattern with Spring Boot.


✅ Step 1: Define the Strategy Interface

public interface DiscountStrategy {
    double applyDiscount(double originalPrice);
    String getCustomerType(); // Used to map the strategy
}

All discount types will implement this interface.


✅ Step 2: Implement Strategies

Regular Customer – no discount

import org.springframework.stereotype.Component;

@Component
public class RegularDiscountStrategy implements DiscountStrategy {

    @Override
    public double applyDiscount(double originalPrice) {
        return originalPrice;
    }

    @Override
    public String getCustomerType() {
        return "REGULAR";
    }
}

Premium Customer – 10% off

import org.springframework.stereotype.Component;

@Component
public class PremiumDiscountStrategy implements DiscountStrategy {

    @Override
    public double applyDiscount(double originalPrice) {
        return originalPrice * 0.90;
    }

    @Override
    public String getCustomerType() {
        return "PREMIUM";
    }
}

VIP Customer – 20% off

import org.springframework.stereotype.Component;

@Component
public class VipDiscountStrategy implements DiscountStrategy {

    @Override
    public double applyDiscount(double originalPrice) {
        return originalPrice * 0.80;
    }

    @Override
    public String getCustomerType() {
        return "VIP";
    }
}

Each class contains its own logic, and you can add more strategies easily.


✅ Step 3: Create the Strategy Context

This class selects the right strategy based on the customer type.

import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class DiscountStrategyContext {

    private final Map<String, DiscountStrategy> strategyMap = new HashMap<>();

    public DiscountStrategyContext(List<DiscountStrategy> strategies) {
        for (DiscountStrategy strategy : strategies) {
            strategyMap.put(strategy.getCustomerType(), strategy);
        }
    }

    public DiscountStrategy getStrategy(String customerType) {
        DiscountStrategy strategy = strategyMap.get(customerType.toUpperCase());
        if (strategy == null) {
            throw new IllegalArgumentException("Unknown customer type: " + customerType);
        }
        return strategy;
    }
}

With Spring's help, all strategy beans are auto-injected and stored in a map.


✅ Step 4: Create the Discount Service

import org.springframework.stereotype.Service;

@Service
public class DiscountService {

    private final DiscountStrategyContext strategyContext;

    public DiscountService(DiscountStrategyContext strategyContext) {
        this.strategyContext = strategyContext;
    }

    public double calculateFinalPrice(String customerType, double originalPrice) {
        DiscountStrategy strategy = strategyContext.getStrategy(customerType);
        return strategy.applyDiscount(originalPrice);
    }
}

This service doesn’t care about how discounts are calculated — it just picks the correct strategy and delegates the call.


✅ Step 5: Create the REST Controller

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/discounts")
public class DiscountController {

    private final DiscountService discountService;

    public DiscountController(DiscountService discountService) {
        this.discountService = discountService;
    }

    @GetMapping
    public ResponseEntity<String> getDiscountedPrice(
            @RequestParam String customerType,
            @RequestParam double price) {

        double finalPrice = discountService.calculateFinalPrice(customerType, price);
        return ResponseEntity.ok("Final price for " + customerType + ": $" + finalPrice);
    }
}

🧪 Test It

Request

GET /api/discounts?customerType=VIP&price=100

Output

Final price for VIP: $80.0

Change customerType to REGULAR or PREMIUM to see different outputs.

✅ No if-else, just clean strategy selection.


Why Use Strategy Pattern in Spring Boot?

Problem Strategy Pattern Fix
Growing if-else blocks Each logic goes in its own class
Hard to test Each strategy is independently testable
Tight coupling Logic is decoupled from service or controller
Hard to extend Just add a new strategy class and Spring picks it up

🔁 More Use Cases for Strategy Pattern

Use Case Description
Payment method routing PayPal, Stripe, UPI, etc.
Tax calculation Based on region or category
Notification sending Email, SMS, push
Shipping fee calculation By weight, speed, or carrier
Report formatting PDF, Excel, HTML

✅ Testing Strategy Logic

You can test strategies like this:

@SpringBootTest
public class VipDiscountStrategyTest {

    @Autowired
    private VipDiscountStrategy strategy;

    @Test
    public void shouldApply20PercentDiscount() {
        double result = strategy.applyDiscount(100.0);
        Assertions.assertEquals(80.0, result);
    }
}

Testing the context:

@SpringBootTest
public class DiscountStrategyContextTest {

    @Autowired
    private DiscountStrategyContext context;

    @Test
    public void shouldReturnPremiumStrategy() {
        DiscountStrategy strategy = context.getStrategy("PREMIUM");
        Assertions.assertTrue(strategy instanceof PremiumDiscountStrategy);
    }
}

✅ Best Practices

  1. Use meaningful strategy names – match with request inputs
  2. Use @Component on each strategy – for auto-injection
  3. Avoid putting logic in the context – let strategies do the work
  4. Validate inputs in each strategy if needed
  5. Write separate tests per strategy – keeps bugs isolated

⚠️ When Not to Use Strategy Pattern

Avoid the Strategy Pattern if:

  • You have only one or two simple conditions
  • The logic won’t change or grow
  • You prefer simple lambdas (for very short logic)

Remember: patterns are tools, not rules. Use them when they help — not when they complicate things.


Summary

Step What We Did
1 Defined a common DiscountStrategy interface
2 Created separate strategy classes for each customer type
3 Built a strategy context to manage strategy selection
4 Delegated logic from service to selected strategy
5 Created a clean controller and tested the full flow

This structure keeps your codebase clean, easy to test, and ready for future growth.


Final Thoughts

The Strategy Pattern is one of the most useful and practical patterns you can apply in a Spring Boot project.

It helps you replace messy conditionals with clean, modular classes — each responsible for a single job. With Spring’s dependency injection, implementing this pattern becomes even simpler.

Start using Strategy Pattern the next time you see multiple if-else branches for business logic. Your future self (and your team) will thank you.

Clean code is simple code. Strategy Pattern helps you get there — one use case at a time.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare