Streamline Your Spring Boot API with Enums and the Strategy Pattern!

Enums in Java are an invaluable resource for simplifying code, enhancing readability, and maintaining type safety in your applications. By leveraging enums alongside the Strategy Pattern, you can reduce complex conditional logic like if-else and switch statements, resulting in a more modular, maintainable, and error-resistant Spring Boot API.

In this article, you'll learn:

  • What Java Enums are and their benefits.

  • How to use Enums with the Strategy Pattern to streamline your API logic.

  • A practical example of using Enums and the Strategy Pattern in a Spring Boot application.


Why Use Enums in Java?

Enums (short for enumerations) are special Java types used to define a set of constants. They:

  • Provide type safety, ensuring only valid values are used.

  • Reduce errors by centralizing constant definitions.

  • Make code more readable and maintainable by eliminating hard-coded strings or numbers.

Example of an Enum:

public enum OrderStatus {
    PENDING,
    COMPLETED,
    CANCELLED
}

Enums can also contain methods, fields, and constructors, making them highly versatile for managing application behavior.


What Is the Strategy Pattern?

The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm or behavior dynamically at runtime. Instead of using conditional statements to decide the logic, you encapsulate each behavior in a separate class and let the application decide which one to use.

When combined with enums, this pattern becomes even more powerful, as enums can map directly to specific strategies.


Example: Using Enums and the Strategy Pattern in Spring Boot

Let’s create an example where we process orders based on their status (PENDING, COMPLETED, or CANCELLED) using enums and the Strategy Pattern.


1. Define the Enum

Each enum constant will map to a specific strategy for handling orders:

public enum OrderStatus {
    PENDING {
        @Override
        public void processOrder() {
            System.out.println("Processing a pending order...");
        }
    },
    COMPLETED {
        @Override
        public void processOrder() {
            System.out.println("Order already completed.");
        }
    },
    CANCELLED {
        @Override
        public void processOrder() {
            System.out.println("Cancelling the order...");
        }
    };

    public abstract void processOrder();
}

Key Points:

  • Each constant overrides the processOrder method.

  • This eliminates the need for a switch statement elsewhere in the code.


2. Define the Strategy Interface (Optional)

If the logic for each status becomes complex, you can decouple it into separate strategy classes.

public interface OrderProcessingStrategy {
    void process();
}

3. Implement Strategies for Each Order Status

Separate the behavior into classes:

public class PendingOrderStrategy implements OrderProcessingStrategy {
    @Override
    public void process() {
        System.out.println("Processing a pending order...");
    }
}

public class CompletedOrderStrategy implements OrderProcessingStrategy {
    @Override
    public void process() {
        System.out.println("Order already completed.");
    }
}

public class CancelledOrderStrategy implements OrderProcessingStrategy {
    @Override
    public void process() {
        System.out.println("Cancelling the order...");
    }
}

4. Map Strategies to Enum Constants

The enum maps directly to strategies:

public enum OrderStatus {
    PENDING(new PendingOrderStrategy()),
    COMPLETED(new CompletedOrderStrategy()),
    CANCELLED(new CancelledOrderStrategy());

    private final OrderProcessingStrategy strategy;

    OrderStatus(OrderProcessingStrategy strategy) {
        this.strategy = strategy;
    }

    public void processOrder() {
        strategy.process();
    }
}

5. Service Layer

Use the enum to process orders dynamically:

import org.springframework.stereotype.Service;

@Service
public class OrderService {
    public void processOrder(OrderStatus status) {
        status.processOrder();
    }
}

6. Controller Layer

Expose an API endpoint to handle orders:

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

@RestController
@RequestMapping("/orders")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping("/process/{status}")
    public String processOrder(@PathVariable OrderStatus status) {
        orderService.processOrder(status);
        return "Order processed with status: " + status;
    }
}

Testing the API

  1. Start your Spring Boot application.

  2. Use Postman or curl to test the endpoint:

    • Process a pending order:

        POST /orders/process/PENDING
      
    • Complete an order:

        POST /orders/process/COMPLETED
      

Benefits of Using Enums with the Strategy Pattern

  1. Code Readability: Logic for each status is encapsulated in one place, avoiding sprawling conditional statements.

  2. Extensibility: Adding a new status or behavior is as simple as adding another enum constant and strategy.

  3. Testability: Each strategy can be tested independently, making the codebase more modular.


Conclusion

Enums and the Strategy Pattern are a powerful combination for simplifying logic in Spring Boot applications. By encapsulating behavior within enums or dedicated strategy classes, you can create clean, maintainable, and extensible code. Start using this approach in your projects to handle complex workflows with ease and precision!

More such articles:

https://medium.com/techwasti

https://www.youtube.com/@maheshwarligade

https://techwasti.com/series/spring-boot-tutorials

https://techwasti.com/series/go-language

0
Subscribe to my newsletter

Read articles from Maheshwar Ligade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maheshwar Ligade
Maheshwar Ligade

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI