Backend Development 10 min read

6 Proven Strategies to Eliminate if‑else in Spring Boot Projects

This article explains why excessive if‑else statements hurt Spring Boot code and presents six practical techniques—including strategy pattern, enums, polymorphism, lambda expressions, command pattern, and guard clauses—along with complete Java examples to make your backend more modular, readable, and maintainable.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
6 Proven Strategies to Eliminate if‑else in Spring Boot Projects

Environment: SpringBoot 3.2.5

Although if‑else statements are ubiquitous, overusing them can make code complex and hard to maintain. This article explores six strategies to reduce if‑else usage in Spring Boot projects, aiming for more modular, readable, and maintainable code.

The six optimization strategies are:

Strategy Pattern

Enum Usage

Polymorphism

Lambda Expressions & Functional Interfaces

Command Pattern

Guard Clauses

2. Practical Cases

2.1 Strategy Pattern

Define a strategy interface for payments:

<code>public interface PaymentStrategy {
    void pay(double amount);
}</code>

Implement different payment methods:

<code>@Component
public class WeiXinPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // TODO
        System.out.printf("WeiXin pay %d%n", amount);
    }
}

@Component
public class AliPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // TODO
        System.out.printf("Ali pay %d%n", amount);
    }
}

@Component
public class BankPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // TODO
        System.out.printf("Bank pay %d%n", amount);
    }
}</code>

Define a payment service that injects all strategies:

<code>@Service
public class PaymentService {
    private final Map<String, PaymentStrategy> paymentStrategies = new HashMap<>();

    public PaymentService(List<PaymentStrategy> strategies) {
        for (PaymentStrategy strategy : strategies) {
            paymentStrategies.put(strategy.getClass().getSimpleName(), strategy);
        }
    }

    public void processPayment(String strategyName, double amount) {
        PaymentStrategy strategy = paymentStrategies.get(strategyName);
        if (strategy != null) {
            strategy.pay(amount);
        } else {
            throw new IllegalArgumentException(String.format("Unsupported payment method: %s", strategyName));
        }
    }
}</code>

2.2 Enum Method

Use an enum to represent order states and their behavior:

<code>public enum OrderStatus {
    NEW {
        @Override
        public void process() { /* TODO */ }
    },
    PAYING {
        @Override
        public void process() { /* TODO */ }
    },
    PAID {
        @Override
        public void process() { /* TODO */ }
    },
    SHIPPED {
        @Override
        public void process() { /* TODO */ }
    };

    public abstract void process();
}</code>

Service that handles orders:

<code>@Service
public class OrderService {
    public void processOrder(OrderStatus status) {
        status.process();
    }
}</code>

2.3 Polymorphism

Define a notification interface and multiple implementations:

<code>public interface Notification {
    void send(String message);
}</code>
<code>@Component
public class EmailNotification implements Notification {
    @Override
    public void send(String message) {
        // TODO: send email
    }
}

@Component
public class SmsNotification implements Notification {
    @Override
    public void send(String message) {
        // TODO: send SMS
    }
}</code>

Service that broadcasts messages to all notifications:

<code>@Service
public class NotificationService {
    private final List<Notification> notifications;

    public NotificationService(List<Notification> notifications) {
        this.notifications = notifications;
    }

    public void notifyAll(String message) {
        for (Notification notification : notifications) {
            notification.send(message);
        }
    }
}</code>

2.4 Lambda Expressions & Functional Interfaces

Use lambda expressions to define discount strategies:

<code>@Service
public class DiscountService {
    private Map<String, Function<Double, Double>> discountStrategies = new HashMap<>();

    public DiscountService() {
        discountStrategies.put("VIP1", price -> price * 0.95);
        discountStrategies.put("VIP2", price -> price * 0.90);
        discountStrategies.put("VIP3", price -> price * 0.85);
        discountStrategies.put("VIP4", price -> price * 0.80);
    }

    public double applyDiscount(String discountCode, double price) {
        return discountStrategies.getOrDefault(discountCode, Function.identity()).apply(price);
    }
}</code>

2.5 Command Pattern

Define a command interface and concrete file operations:

<code>public interface Command {
    void execute();
}</code>
<code>@Component
public class OpenFileCommand implements Command {
    private FileSystemReceiver fileSystem;

    public OpenFileCommand(FileSystemReceiver fs) {
        this.fileSystem = fs;
    }

    @Override
    public void execute() {
        fileSystem.openFile();
    }
}

@Component
public class CloseFileCommand implements Command {
    private final FileSystemReceiver fileSystem;

    public CloseFileCommand(FileSystemReceiver fileSystem) {
        this.fileSystem = fileSystem;
    }

    @Override
    public void execute() {
        fileSystem.closeFile();
    }
}</code>

Receiver interface and implementation:

<code>public interface FileSystemReceiver {
    void openFile();
    void closeFile();
}

@Service
public class UnixFileSystemReceiver implements FileSystemReceiver {
    @Override
    public void openFile() { /* TODO */ }
    @Override
    public void closeFile() { /* TODO */ }
}</code>

Invoker that runs a command:

<code>@Component
public class FileInvoker {
    private final Command command;

    public FileInvoker(Command command) {
        this.command = command;
    }

    public void execute() {
        command.execute();
    }
}</code>

2.6 Guard Clause

Validate input early to keep the main logic clean:

<code>@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void registerUser(User user) {
        if (user == null) {
            throw new IllegalArgumentException("User cannot be null");
        }
        if (!StringUtils.hasLength(user.getName())) {
            throw new IllegalArgumentException("User name cannot be empty");
        }
        if (!StringUtils.hasLength(user.getEmail())) {
            throw new IllegalArgumentException("User email cannot be empty");
        }
        userRepository.save(user);
    }
}</code>

Conclusion: Applying these six strategies can significantly reduce the use of if‑else statements in Spring Boot projects, resulting in cleaner, more efficient, and more maintainable code.

backenddesign patternsrefactoringspring-boot
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.