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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
