在实际的业务开发当中,经常会遇到复杂的业务逻辑,可能部分同学实现出来的代码并没有什么问题,但是代码的可读性很差。
本篇文章主要总结一下自己在实际开发中如何避免大面积的 if-else 代码块的问题。补充说明一点,不是说 if-else 不好,而是多层嵌套的 if-else 导致代码可读性差、维护成本高等问题。
现有如下一段示例代码,部分优化技巧是根据这段代码进行的:
public class BadCodeDemo { private void getBadCodeBiz(Integer city, List newDataList, List 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 oldCollect = oldDataList.stream().filter(p -> { if (p.getIsHoliday() == 1) { return true; } return false; }).collect(Collectors.toList()); List 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()); } } } } } } } }
技巧一:提取方法,拆分逻辑✦
比如上面这段代码中:
if(null != city) { } else { }
这里可以拆分成两段逻辑,核心思想就是逻辑单元最小化,然后合并逻辑单元。
private void getCityNotNull(Integer city, List newDataList) { 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); } } } // 合并逻辑流程 private void getBadCodeBiz(Integer city, List newDataList, List oldDataList) { if (city != null) { this.getCityNull(city, newDataList); } else { //此处代码省略 } }
技巧二:分支逻辑提前return✦
比如“技巧一”中的 getCityNull 方法,我们可以这样写:
public void getCityNotNull(Integer city, List newDataList) { if (CollectionUtils.isEmpty(newDataList)) { // 提前判断,返回业务逻辑 return; } TestCodeData newData = newDataList.stream().filter(p -> { if (p.getIsHoliday() == 1) { return true; } return false; }).findFirst().orElse(null); if (null != newData) { newData.setCity(city); } }
技巧三:枚举✦
经过“技巧一”和“技巧二”的优化,文章开头的这段代码被优化成如下所示:
public class BadCodeDemo { public void getBadCodeBiz(Integer city, List newDataList, List oldDataList) { if (city != null) { this.getCityNotNull(city, newDataList); } else { this.getCityNull(newDataList, oldDataList); } } private void getCityNotNull(Integer city, List newDataList) { if (CollectionUtils.isEmpty(newDataList)) { // 提前判断,返回业务逻辑 return; } TestCodeData newData = newDataList.stream().filter(p -> { if (p.getIsHoliday() == 1) { return true; } return false; }).findFirst().orElse(null); if (null != newData) { newData.setCity(city); } } private void getCityNull(List newDataList, List oldDataList) { // 提前判断,返回业务逻辑 if (CollectionUtils.isEmpty(oldDataList) && CollectionUtils.isEmpty(newDataList)) { return; } List oldCollect = oldDataList.stream().filter(p -> { if (p.getIsHoliday() == 1) { return true; } return false; }).collect(Collectors.toList()); List newCollect = newDataList.stream().filter(p -> { if (p.getIsHoliday() == 1) { return true; } return false; }).collect(Collectors.toList()); // 提前判断,返回业务逻辑 if (CollectionUtils.isEmpty(newCollect) && CollectionUtils.isEmpty(oldCollect)) { return; } 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()); } } } } }
现在利用“枚举”来优化 getCityNull 方法中的 for 循环部分代码,我们可以看到这段代码中有 4 段逻辑,总体形式如下:
if (newPO.getStartTime() == 0 && newPO.getEndTime() == 12) { //第一段逻辑 } else if (newPO.getStartTime() == 12 && newPO.getEndTime() == 24) { //第二段逻辑 } else if (newPO.getStartTime() == 0 && newPO.getEndTime() == 24) { //第三段逻辑 } else if (newPO.getTimeUnit().equals(Integer.valueOf(1))) { //第四段逻辑 }
按照这个思路利用枚举进行二次优化,将其中的逻辑封装到枚举类中:
public enum TimeEnum { AM("am", "上午") { @Override public void setCity(TestCodeData data, List 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 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 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 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 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; } }
然后 getCityNull 方法中 for 循环部分逻辑如下:
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); } }
其实在这个业务场景中使用枚举并不是特别合适,如果在遍历对象时,我们就知道要执行哪个枚举类型,此时最合适,伪代码如下:
for (TestCodeData data : newCollect) { String code = "am"; // 这里假设 code 变量是从 data 中获取的 TimeEnum.valueOf(code).setCity(data, oldCollect); }
技巧四:函数式接口✦
业务场景描述:比如让你做一个简单的营销拉新活动,这个活动投放到不同的渠道,不同渠道过来的用户奖励不一样。
现假设在头条、微信等渠道都投放了该活动。此时你的代码可能会写出如下形式:
@RestController @RequestMapping("/activity") public class ActivityController { @Resource private AwardService awardService; @PostMapping("/reward") public void reward(String userId, String source) { if ("toutiao".equals(source)) { awardService.toutiaoReward(userId); } else if ("wx".equals(source)) { awardService.wxReward(userId); } } } @Service public class AwardService { private static final Logger log = LoggerFactory.getLogger(AwardService.class); public Boolean toutiaoReward(String userId) { log.info("头条渠道用户{}奖励50元红包!", userId); return Boolean.TRUE; } public Boolean wxReward(String userId) { log.info("微信渠道用户{}奖励100元红包!", userId); return Boolean.TRUE; } }
看完这段代码,逻辑上是没有什么问题的。但它有一个隐藏的缺陷,如果后期又增加很多渠道的时候,你该怎么办?继续 else if 吗?
其实我们可以利用函数式接口优化,当然设计模式也可以优化。这里我只是举例使用一下函数式接口的使用方式。
@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关注打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?


微信扫码登录