How to Refactor Complex if‑else Logic in Java: 5 Practical Techniques

This article explains why deeply nested if‑else statements hurt readability and maintenance, then presents five practical techniques—including method extraction, early returns, enums, functional interfaces, and design patterns—along with complete Java code examples to transform messy business logic into clean, modular code.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
How to Refactor Complex if‑else Logic in Java: 5 Practical Techniques

Why clean if‑else matters

In real business development, complex nested if‑else blocks often work correctly but are hard to read and maintain.

Technique 1 – Extract methods and split logic

Separate each logical unit into its own method to minimise nesting and improve clarity.

public class BadCodeDemo {
    private void getBadCodeBiz(Integer city, List<TestCodeData> newDataList, List<TestCodeData> oldDataList) {
        if (city != null) {
            if (newDataList != null && newDataList.size() > 0) {
                TestCodeData newData = newDataList.stream().filter(p -> {
                    if (p.getIsHoliday() == 1) {
                        return true;
                    }
                    return false;
                }).findFirst().orElse(null);
                if (newData != null) {
                    newData.setCity(city);
                }
            }
        } else {
            if (oldDataList != null && newDataList != null) {
                List<TestCodeData> oldCollect = oldDataList.stream().filter(p -> {
                    if (p.getIsHoliday() == 1) {
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
                List<TestCodeData> newCollect = newDataList.stream().filter(p -> {
                    if (p.getIsHoliday() == 1) {
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
                if (newCollect != null && newCollect.size() > 0 && oldCollect != null && oldCollect.size() > 0) {
                    for (TestCodeData newPO : newCollect) {
                        if (newPO.getStartTime() == 0 && newPO.getEndTime() == 12) {
                            TestCodeData po = oldCollect.stream().filter(p -> p.getStartTime() == 0 && (p.getEndTime() == 12 || p.getEndTime() == 24)).findFirst().orElse(null);
                            if (po != null) {
                                newPO.setCity(po.getCity());
                            }
                        } else if (newPO.getStartTime() == 12 && newPO.getEndTime() == 24) {
                            TestCodeData po = oldCollect.stream().filter(p -> (p.getStartTime() == 12 || p.getStartTime() == 0) && p.getEndTime() == 24).findFirst().orElse(null);
                            if (po != null) {
                                newPO.setCity(po.getCity());
                            }
                        } else if (newPO.getStartTime() == 0 && newPO.getEndTime() == 24) {
                            TestCodeData po = oldCollect.stream().filter(p -> p.getStartTime() == 0 && p.getEndTime() == 24).findFirst().orElse(null);
                            if (po == null) {
                                po = oldCollect.stream().filter(p -> p.getStartTime() == 0 && p.getEndTime() == 12).findFirst().orElse(null);
                            }
                            if (po == null) {
                                po = oldCollect.stream().filter(p -> p.getStartTime() == 12 && p.getEndTime() == 24).findFirst().orElse(null);
                            }
                            if (po != null) {
                                newPO.setCity(po.getCity());
                            }
                        } else if (newPO.getTimeUnit().equals(Integer.valueOf(1))) {
                            TestCodeData po = oldCollect.stream().filter(e -> e.getTimeUnit().equals(Integer.valueOf(1))).findFirst().orElse(null);
                            if (po != null) {
                                newPO.setCity(po.getCity());
                            }
                        }
                    }
                }
            }
        }
    }
}

Technique 2 – Early return in branch logic

Return as soon as a condition is met to avoid deep nesting.

public void getCityNotNull(Integer city, List<TestCodeData> newDataList) {
    if (CollectionUtils.isEmpty(newDataList)) {
        // early exit
        return;
    }
    TestCodeData newData = newDataList.stream().filter(p -> {
        if (p.getIsHoliday() == 1) {
            return true;
        }
        return false;
    }).findFirst().orElse(null);
    if (null != newData) {
        newData.setCity(city);
    }
}

Technique 3 – Use enums to encapsulate branching

Define an enum where each constant implements the specific logic, removing the long if‑else chain.

public enum TimeEnum {
    AM("am", "上午") {
        @Override
        public void setCity(TestCodeData data, List<TestCodeData> oldDataList) {
            TestCodeData po = oldDataList.stream()
                .filter(p -> p.getStartTime() == 0 && (p.getEndTime() == 12 || p.getEndTime() == 24))
                .findFirst().orElse(null);
            if (null != po) {
                data.setCity(po.getCity());
            }
        }
    },
    PM("pm", "下午") {
        @Override
        public void setCity(TestCodeData data, List<TestCodeData> oldCollect) {
            TestCodeData po = oldCollect.stream()
                .filter(p -> (p.getStartTime() == 12 || p.getStartTime() == 0) && p.getEndTime() == 24)
                .findFirst().orElse(null);
            if (po != null) {
                data.setCity(po.getCity());
            }
        }
    },
    DAY("day", "全天") {
        @Override
        public void setCity(TestCodeData data, List<TestCodeData> oldCollect) {
            TestCodeData po = oldCollect.stream()
                .filter(p -> p.getStartTime() == 0 && p.getEndTime() == 24)
                .findFirst().orElse(null);
            if (po == null) {
                po = oldCollect.stream()
                    .filter(p -> p.getStartTime() == 0 && p.getEndTime() == 12)
                    .findFirst().orElse(null);
            }
            if (po == null) {
                po = oldCollect.stream()
                    .filter(p -> p.getStartTime() == 12 && p.getEndTime() == 24)
                    .findFirst().orElse(null);
            }
            if (po != null) {
                data.setCity(po.getCity());
            }
        }
    },
    HOUR("hour", "小时") {
        @Override
        public void setCity(TestCodeData data, List<TestCodeData> oldCollect) {
            TestCodeData po = oldCollect.stream()
                .filter(e -> e.getTimeUnit().equals(Integer.valueOf(1)))
                .findFirst().orElse(null);
            if (po != null) {
                data.setCity(po.getCity());
            }
        }
    };

    public abstract void setCity(TestCodeData data, List<TestCodeData> oldCollect);
    private String code;
    private String desc;
    TimeEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    public String getCode() { return code; }
    public void setCode(String code) { this.code = code; }
    public String getDesc() { return desc; }
    public void setDesc(String desc) { this.desc = desc; }
}

Usage in the original loop becomes a simple enum call:

for (TestCodeData data : newCollect) {
    if (data.getStartTime() == 0 && data.getEndTime() == 12) {
        TimeEnum.AM.setCity(data, oldCollect);
    } else if (data.getStartTime() == 12 && data.getEndTime() == 24) {
        TimeEnum.PM.setCity(data, oldCollect);
    } else if (data.getStartTime() == 0 && data.getEndTime() == 24) {
        TimeEnum.DAY.setCity(data, oldCollect);
    } else if (data.getTimeUnit().equals(Integer.valueOf(1))) {
        TimeEnum.HOUR.setCity(data, oldCollect);
    }
}

Technique 4 – Functional interfaces for channel‑based rewards

Replace long if‑else chains that dispatch rewards based on source with a map of BiFunction objects.

@RestController
@RequestMapping("/activity")
public class ActivityController {
    @Resource
    private AwardService awardService;
    @PostMapping("/reward")
    public void reward(String userId, String source) {
        awardService.getRewardResult(userId, source);
    }
}

@Service
public class AwardService {
    private static final Logger log = LoggerFactory.getLogger(AwardService.class);
    private Map<String, BiFunction<String, String, Boolean>> sourceMap = new HashMap<>();
    @PostConstruct
    private void dispatcher() {
        sourceMap.put("wx", (uid, src) -> this.wxReward(uid));
        sourceMap.put("toutiao", (uid, src) -> this.toutiaoReward(uid));
    }
    public Boolean getRewardResult(String userId, String source) {
        BiFunction<String, String, Boolean> result = sourceMap.get(source);
        if (null != result) {
            return result.apply(userId, source);
        }
        return Boolean.FALSE;
    }
    private Boolean toutiaoReward(String userId) {
        log.info("头条渠道用户{}奖励50元红包!", userId);
        return Boolean.TRUE;
    }
    private Boolean wxReward(String userId) {
        log.info("微信渠道用户{}奖励100元红包!", userId);
        return Boolean.TRUE;
    }
}

Technique 5 – Design patterns (Strategy, Template Method, Factory, Singleton)

Define an abstract award class, concrete strategy implementations, a factory to obtain the right strategy, and a singleton holder for the factory.

public abstract class AwardAbstract {
    public abstract Boolean award(String userId);
}

public class TouTiaoAwardService extends AwardAbstract {
    @Override
    public Boolean award(String userId) {
        log.info("头条渠道用户{}奖励50元红包!", userId);
        return Boolean.TRUE;
    }
}

public class WeChatAwardService extends AwardAbstract {
    @Override
    public Boolean award(String userId) {
        log.info("微信渠道用户{}奖励100元红包!", userId);
        return Boolean.TRUE;
    }
}

public class AwardFactory {
    public static AwardAbstract getAwardInstance(String source) {
        if ("toutiao".equals(source)) {
            return new TouTiaoAwardService();
        } else if ("wx".equals(source)) {
            return new WeChatAwardService();
        }
        return null;
    }
}

@RestController
@RequestMapping("/activity")
public class ActivityController {
    @PostMapping("/reward2")
    public void reward2(String userId, String source) {
        AwardAbstract instance = AwardFactory.getAwardInstance(source);
        if (instance != null) {
            instance.award(userId);
        }
    }
}

Further, a strategy‑based template method adds authentication and risk checks before the actual award:

public interface AwardStrategy {
    Boolean awardStrategy(String userId);
    String getSource();
}

public abstract class BaseAwardTemplate {
    private static final Logger log = LoggerFactory.getLogger(BaseAwardTemplate.class);
    public Boolean awardTemplate(String userId) {
        authentication(userId);
        risk(userId);
        return awardRecord(userId);
    }
    protected void authentication(String userId) { log.info("{} 执行身份验证!", userId); }
    protected void risk(String userId) { log.info("{} 执行风控校验!", userId); }
    protected abstract Boolean awardRecord(String userId);
}

@Service
public class ToutiaoAwardStrategyService extends BaseAwardTemplate implements AwardStrategy {
    @Override
    public Boolean awardStrategy(String userId) { return super.awardTemplate(userId); }
    @Override
    public String getSource() { return "toutiao"; }
    @Override
    protected Boolean awardRecord(String userId) {
        log.info("头条渠道用户{}奖励50元红包!", userId);
        return Boolean.TRUE;
    }
}

@Service
public class WeChatAwardStrategyService extends BaseAwardTemplate implements AwardStrategy {
    @Override
    public Boolean awardStrategy(String userId) { return super.awardTemplate(userId); }
    @Override
    public String getSource() { return "wx"; }
    @Override
    protected Boolean awardRecord(String userId) {
        log.info("微信渠道用户{}奖励100元红包!", userId);
        return Boolean.TRUE;
    }
}

@Component
public class AwardStrategyFactory implements ApplicationContextAware {
    private static final Map<String, AwardStrategy> MAP = new HashMap<>();
    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        ctx.getBeansOfType(AwardStrategy.class).values().forEach(s -> MAP.put(s.getSource(), s));
    }
    public Boolean getAwardResult(String userId, String source) {
        AwardStrategy strategy = MAP.get(source);
        if (strategy == null) throw new RuntimeException("渠道异常!");
        return strategy.awardStrategy(userId);
    }
    private static class CreateFactorySingleton {
        private static final AwardStrategyFactory factory = new AwardStrategyFactory();
    }
    public static AwardStrategyFactory getInstance() { return CreateFactorySingleton.factory; }
}

@RestController
@RequestMapping("/activity")
public class ActivityController {
    @PostMapping("/reward3")
    public void reward3(String userId, String source) {
        AwardStrategyFactory.getInstance().getAwardResult(userId, source);
    }
}

Other useful tips

Use the ternary operator for simple conditional assignments.

Extract reusable business logic into shared methods to avoid duplication.

Regardless of the technique you choose, always think about how to express complex business rules with concise, readable code rather than falling into a CRUD‑only mindset.

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.

Design PatternsJavacode readability
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

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.