Refactoring Java Spring Boot Code: Eliminating If-Else Statements for Cleaner, Extensible Logic

Naveen Metta
5 min readJun 17, 2024

--

credit goes to the owner : https://www.freecodecamp.org/news/clean-coding-for-beginners/
source : freecodecamp.org

When working with Java Spring Boot applications, one common issue developers face is the overuse of if-else statements. While if-else logic is often the simplest way to implement conditional behavior, it can lead to code that is difficult to read, maintain, and extend. This article will explore various strategies to refactor if-else statements, leading to cleaner and more extensible code.

Why Avoid If-Else Statements?

1. Readability:

  • If-else chains can be difficult to follow, especially when they grow in size.
  • Nested if-else statements further complicate understanding.

2. Maintainability:

  • Changes in logic often require modifying multiple conditions scattered throughout the code.
  • The likelihood of introducing bugs increases with each new condition.

3. Extensibility:

  • Adding new conditions means adding more if-else statements, which can become cumbersome.
  • If-else logic is not easily adaptable to changing requirements.

Strategies for Eliminating If-Else Statements

1. Polymorphism

Polymorphism allows us to use a common interface or superclass to define methods that are implemented by different classes. This technique is particularly useful when the conditional logic is based on type.

Example:

Consider a simple example of a payment processing system:

public interface PaymentProcessor {
void processPayment(double amount);
}

public class CreditCardProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
// Logic for processing credit card payment
System.out.println("Processing credit card payment: $" + amount);
}
}

public class PayPalProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
// Logic for processing PayPal payment
System.out.println("Processing PayPal payment: $" + amount);
}
}

Without polymorphism, you might use an if-else statement:

public void processPayment(String method, double amount) {
if ("creditCard".equals(method)) {
// Credit card processing logic
} else if ("paypal".equals(method)) {
// PayPal processing logic
}
}

Refactored using polymorphism:

public class PaymentService {
private Map<String, PaymentProcessor> processors = new HashMap<>();

public PaymentService() {
processors.put("creditCard", new CreditCardProcessor());
processors.put("paypal", new PayPalProcessor());
}

public void processPayment(String method, double amount) {
PaymentProcessor processor = processors.get(method);
if (processor != null) {
processor.processPayment(amount);
} else {
throw new IllegalArgumentException("Unsupported payment method: " + method);
}
}
}

2. Strategy Pattern

The Strategy Pattern is a behavioral design pattern that allows selecting an algorithm’s behavior at runtime. It encapsulates algorithms inside a class and makes them interchangeable.

Example:

Consider a discount calculation system where different strategies apply based on customer type:

public interface DiscountStrategy {
double applyDiscount(double price);
}

public class RegularCustomerDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.95; // 5% discount
}
}

public class PremiumCustomerDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.90; // 10% discount
}
}

public class NoDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price;
}
}

Instead of using an if-else chain:

public double calculateDiscount(String customerType, double price) {
if ("regular".equals(customerType)) {
return price * 0.95;
} else if ("premium".equals(customerType)) {
return price * 0.90;
} else {
return price;
}
}

Refactor using the Strategy Pattern:

public class DiscountService {
private Map<String, DiscountStrategy> strategies = new HashMap<>();

public DiscountService() {
strategies.put("regular", new RegularCustomerDiscount());
strategies.put("premium", new PremiumCustomerDiscount());
strategies.put("none", new NoDiscount());
}

public double calculateDiscount(String customerType, double price) {
DiscountStrategy strategy = strategies.getOrDefault(customerType, new NoDiscount());
return strategy.applyDiscount(price);
}
}

3. Command Pattern

The Command Pattern is a behavioral design pattern that turns a request into a stand-alone object containing all the information about the request. This approach allows parameterizing clients with queues, requests, and operations.

Example:

Consider an order processing system where different commands are executed based on order type:

public interface OrderCommand {
void execute();
}

public class BuyOrderCommand implements OrderCommand {
@Override
public void execute() {
// Logic for buy order
System.out.println("Executing buy order.");
}
}

public class SellOrderCommand implements OrderCommand {
@Override
public void execute() {
// Logic for sell order
System.out.println("Executing sell order.");
}
}

Without the Command Pattern, you might use if-else:

public void processOrder(String orderType) {
if ("buy".equals(orderType)) {
// Buy order logic
} else if ("sell".equals(orderType)) {
// Sell order logic
}
}

Refactor using the Command Pattern:

public class OrderService {
private Map<String, OrderCommand> commands = new HashMap<>();

public OrderService() {
commands.put("buy", new BuyOrderCommand());
commands.put("sell", new SellOrderCommand());
}

public void processOrder(String orderType) {
OrderCommand command = commands.get(orderType);
if (command != null) {
command.execute();
} else {
throw new IllegalArgumentException("Unsupported order type: " + orderType);
}
}
}

4. Factory Pattern

The Factory Pattern is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.

Example:

Consider a vehicle manufacturing system where different factories produce different types of vehicles:

public abstract class Vehicle {
public abstract void manufacture();
}

public class Car extends Vehicle {
@Override
public void manufacture() {
System.out.println("Manufacturing Car");
}
}

public class Bike extends Vehicle {
@Override
public void manufacture() {
System.out.println("Manufacturing Bike");
}
}

Without the Factory Pattern, you might use an if-else chain:

public Vehicle createVehicle(String type) {
if ("car".equals(type)) {
return new Car();
} else if ("bike".equals(type)) {
return new Bike();
} else {
throw new IllegalArgumentException("Unknown vehicle type: " + type);
}
}

Refactor using the Factory Pattern:

public abstract class VehicleFactory {
public abstract Vehicle createVehicle();
}

public class CarFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Car();
}
}

public class BikeFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Bike();
}
}

public class VehicleService {
private Map<String, VehicleFactory> factories = new HashMap<>();

public VehicleService() {
factories.put("car", new CarFactory());
factories.put("bike", new BikeFactory());
}

public Vehicle createVehicle(String type) {
VehicleFactory factory = factories.get(type);
if (factory != null) {
return factory.createVehicle();
} else {
throw new IllegalArgumentException("Unknown vehicle type: " + type);
}
}
}

5. Enum with Function Map

Enums with function maps are a way to use enums to hold different behaviors associated with constants, which can replace complex if-else chains.

Example:

Consider a task management system where different tasks have different behaviors:

public enum TaskType {
EMAIL {
@Override
public void execute() {
System.out.println("Executing email task.");
}
},
SMS {
@Override
public void execute() {
System.out.println("Executing SMS task.");
}
};

public abstract void execute();
}

Without enums, you might use an if-else chain:

public void executeTask(String taskType) {
if ("email".equals(taskType)) {
// Email task logic
} else if ("sms".equals(taskType)) {
// SMS task logic
}
}

Refactor using enum with function map:

public class TaskService {
public void executeTask(TaskType taskType) {
taskType.execute();
}
}

Conclusion

Eliminating if-else statements in your Java Spring Boot code can significantly improve readability, maintainability, and extensibility. By leveraging design patterns such as Polymorphism, Strategy Pattern, Command Pattern, Factory Pattern, and Enums with Function Maps, you can write cleaner and more maintainable code.

Each of these strategies has its own use cases and benefits. Understanding when and how to apply them will help you become a more effective Java developer, capable of writing code that is both elegant and robust.

--

--

Naveen Metta

I'm a Full Stack Developer with 2.5 years of experience. feel free to reach out for any help : mettanaveen701@gmail.com