Eliminate Long if‑else Chains in Java: Design Patterns & Spring Solutions

This article examines the drawbacks of lengthy if‑else statements in Java payment services, explains how they violate the Open/Closed and Single Responsibility principles, and presents multiple refactoring techniques—including custom annotations, dynamic bean naming, template methods, strategy‑factory, and chain‑of‑responsibility patterns—to produce cleaner, more maintainable backend code.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Eliminate Long if‑else Chains in Java: Design Patterns & Spring Solutions

Introduction

During a recent code refactor the author discovered many smelly, overly long if…else blocks and decided to demonstrate how to refactor them.

Problematic if…else example

public interface IPay {
    void pay();
}

@Service
public class AliaPay implements IPay {
    @Override
    public void pay() {
        System.out.println("===发起支付宝支付===");
    }
}

@Service
public class WeixinPay implements IPay {
    @Override
    public void pay() {
        System.out.println("===发起微信支付===");
    }
}

@Service
public class JingDongPay implements IPay {
    @Override
    public void pay() {
        System.out.println("===发起京东支付===");
    }
}

@Service
public class PayService {
    @Autowired private AliaPay aliaPay;
    @Autowired private WeixinPay weixinPay;
    @Autowired private JingDongPay jingDongPay;

    public void toPay(String code) {
        if ("alia".equals(code)) {
            aliaPay.pay();
        } else if ("weixin".equals(code)) {
            weixinPay.pay();
        } else if ("jingdong".equals(code)) {
            jingDongPay.pay();
        } else {
            System.out.println("找不到支付方式");
        }
    }
}

The toPay method violates the Open/Closed principle and the Single Responsibility principle because every new payment method requires modifying this method.

Open/Closed principle: software should be open for extension but closed for modification.
Single Responsibility principle: a class should have only one reason to change.

Solutions to eliminate if…else

1. Use annotations

Define a custom annotation that binds a payment code to a class.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PayCode {
    String value();
    String name();
}

Annotate each payment implementation.

@PayCode(value = "alia", name = "支付宝支付")
@Service
public class AliaPay implements IPay {
    @Override
    public void pay() {
        System.out.println("===发起支付宝支付===");
    }
}

@PayCode(value = "weixin", name = "微信支付")
@Service
public class WeixinPay implements IPay {
    @Override
    public void pay() {
        System.out.println("===发起微信支付===");
    }
}

@PayCode(value = "jingdong", name = "京东支付")
@Service
public class JingDongPay implements IPay {
    @Override
    public void pay() {
        System.out.println("===发起京东支付===");
    }
}

Scan the application context for beans with the annotation and build a map.

@Service
public class PayService2 implements ApplicationListener<ContextRefreshedEvent> {
    private static Map<String, IPay> payMap = null;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext ctx = event.getApplicationContext();
        Map<String, Object> beans = ctx.getBeansWithAnnotation(PayCode.class);
        if (beans != null) {
            payMap = new HashMap<>();
            beans.forEach((k, v) -> {
                String bizType = v.getClass().getAnnotation(PayCode.class).value();
                payMap.put(bizType, (IPay) v);
            });
        }
    }

    public void pay(String code) {
        payMap.get(code).pay();
    }
}

2. Dynamic bean name concatenation

Use the code to construct the bean name (e.g., aliaPay) and retrieve it from the context.

@Service
public class PayService3 implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    private static final String SUFFIX = "Pay";

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        this.applicationContext = ctx;
    }

    public void toPay(String payCode) {
        ((IPay) applicationContext.getBean(getBeanName(payCode))).pay();
    }

    public String getBeanName(String payCode) {
        return payCode + SUFFIX;
    }
}

3. Template method (support method)

Introduce a support method in the IPay interface so each implementation decides whether it can handle the code.

public interface IPay {
    boolean support(String code);
    void pay();
}

@Service
public class AliaPay implements IPay {
    @Override
    public boolean support(String code) { return "alia".equals(code); }
    @Override
    public void pay() { System.out.println("===发起支付宝支付==="); }
}

@Service
public class WeixinPay implements IPay {
    @Override
    public boolean support(String code) { return "weixin".equals(code); }
    @Override
    public void pay() { System.out.println("===发起微信支付==="); }
}

@Service
public class JingDongPay implements IPay {
    @Override
    public boolean support(String code) { return "jingdong".equals(code); }
    @Override
    public void pay() { System.out.println("===发起京东支付==="); }
}

Iterate the list of IPay beans and invoke the first that supports the code.

@Service
public class PayService4 implements ApplicationContextAware, InitializingBean {
    private ApplicationContext applicationContext;
    private List<IPay> payList = null;

    @Override
    public void afterPropertiesSet() throws Exception {
        if (payList == null) {
            payList = new ArrayList<>();
            Map<String, IPay> beans = applicationContext.getBeansOfType(IPay.class);
            beans.forEach((k, v) -> payList.add(v));
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        this.applicationContext = ctx;
    }

    public void toPay(String code) {
        for (IPay iPay : payList) {
            if (iPay.support(code)) { iPay.pay(); break; }
        }
    }
}

4. Strategy + Factory pattern

Each payment class registers itself in a global factory.

public interface IPay { void pay(); }

@Service
public class AliaPay implements IPay {
    @PostConstruct
    public void init() { PayStrategyFactory.register("aliaPay", this); }
    @Override public void pay() { System.out.println("===发起支付宝支付==="); }
}

@Service
public class WeixinPay implements IPay {
    @PostConstruct
    public void init() { PayStrategyFactory.register("weixinPay", this); }
    @Override public void pay() { System.out.println("===发起微信支付==="); }
}

@Service
public class JingDongPay implements IPay {
    @PostConstruct
    public void init() { PayStrategyFactory.register("jingDongPay", this); }
    @Override public void pay() { System.out.println("===发起京东支付==="); }
}

public class PayStrategyFactory {
    private static final Map<String, IPay> PAY_REGISTERS = new HashMap<>();
    public static void register(String code, IPay iPay) { if (code != null && !"".equals(code)) PAY_REGISTERS.put(code, iPay); }
    public static IPay get(String code) { return PAY_REGISTERS.get(code); }
}

@Service
public class PayService3 {
    public void toPay(String code) { PayStrategyFactory.get(code).pay(); }
}

5. Chain of Responsibility

Define an abstract handler with a reference to the next handler.

public abstract class PayHandler {
    protected PayHandler next;
    public abstract void pay(String code);
}

@Service
public class AliaPayHandler extends PayHandler {
    @Override
    public void pay(String code) {
        if ("alia".equals(code)) {
            System.out.println("===发起支付宝支付===");
        } else {
            next.pay(code);
        }
    }
}

@Service
public class WeixinPayHandler extends PayHandler {
    @Override
    public void pay(String code) {
        if ("weixin".equals(code)) {
            System.out.println("===发起微信支付===");
        } else {
            next.pay(code);
        }
    }
}

@Service
public class JingDongPayHandler extends PayHandler {
    @Override
    public void pay(String code) {
        if ("jingdong".equals(code)) {
            System.out.println("===发起京东支付===");
        } else {
            next.pay(code);
        }
    }
}

@Service
public class PayHandlerChain implements ApplicationContextAware, InitializingBean {
    private ApplicationContext applicationContext;
    private PayHandler header;

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException { this.applicationContext = ctx; }

    @Override
    public void afterPropertiesSet() throws Exception {
        Map<String, PayHandler> beans = applicationContext.getBeansOfType(PayHandler.class);
        if (beans == null || beans.isEmpty()) return;
        List<PayHandler> handlers = new ArrayList<>(beans.values());
        for (int i = 0; i < handlers.size() - 1; i++) {
            handlers.get(i).setNext(handlers.get(i + 1));
        }
        header = handlers.get(0);
    }

    public void handlePay(String code) { header.pay(code); }
}

6. Other simplifications

Replace multiple if…else branches with an enum, use Java 8 streams for collection filtering, apply the ternary operator for simple decisions, and leverage Assert in Spring for parameter validation.

public enum MessageEnum {
    SUCCESS(1, "成功"),
    FAIL(-1, "失败"),
    TIME_OUT(-2, "网络超时"),
    PARAM_ERROR(-3, "参数错误");
    private final int code;
    private final String message;
    MessageEnum(int code, String message) { this.code = code; this.message = message; }
    public int getCode() { return code; }
    public String getMessage() { return message; }
    public static MessageEnum of(int code) {
        return Arrays.stream(values()).filter(e -> e.code == code).findFirst().orElse(null);
    }
}

public String getMessage(int code) {
    MessageEnum e = MessageEnum.of(code);
    return e != null ? e.getMessage() : "未知错误";
}

public void save(Integer code, String name) {
    Assert.notNull(code, "code不能为空");
    Assert.notNull(name, "name不能为空");
    System.out.println("doSave");
}

These techniques collectively remove the need for long if…else chains, improve extensibility, and keep the codebase clean.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

springstrategyif-else
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

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.