实战技巧:if-else代码优化技巧(if else如何优化)

在实际的业务开发当中,经常会遇到复杂的业务逻辑,可能部分同学实现出来的代码并没有什么问题,但是代码的可读性很差。本篇文章主要总结一下自己在实际开发中如何避免大面积的 if-else 代码块的问题。补充说明一点,不是说 if-else 不好,而是多层嵌套的 if-else 导致代码可读性差、维护成本高等问题。
  现有如下一段示例代码,部分优化技巧是根据这段代码进行的

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()); } } } } } } }}复制代码

技巧一:提取方法,拆分逻辑

比如上面这段代码中

if(null != city) {} else {}复制代码

这里可以拆分成两段逻辑,核心思想就是逻辑单元最小化,然后合并逻辑单元。

private void getCityNotNull(Integer city, List<TestCodeData> 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<TestCodeData> newDataList, List<TestCodeData> oldDataList) { if (city != null) { this.getCityNull(city, newDataList); } else { //此处代码省略 }}复制代码

技巧二:分支逻辑提前return

比如 技巧一 中的 getCityNull 方法,我们可以这样写

public void getCityNotNull(Integer city, List<TestCodeData> 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<TestCodeData> newDataList, List<TestCodeData> oldDataList) { if (city != null) { this.getCityNotNull(city, newDataList); } else { this.getCityNull(newDataList, oldDataList); } } private void getCityNotNull(Integer city, List<TestCodeData> 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<TestCodeData> newDataList, List<TestCodeData> oldDataList) { // 提前判断,返回业务逻辑 if (CollectionUtils.isEmpty(oldDataList) && CollectionUtils.isEmpty(newDataList)) { return; } 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 (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<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; }}复制代码

然后 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); } }}@Servicepublic 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); }}@Servicepublic 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", (userId, source) -> this.wxReward(userId)); sourceMap.put("toutiao", (userId, source) -> this.toutiaoReward(userId)); } 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; }}复制代码

针对一些复杂的业务场景,业务参数很多时,可以利用 @FunctionalInterface 自定义函数式接口来满足你的业务需求,使用原理和本例并无差别。

技巧五:设计模式

  设计模式对于if-else的优化,我个人觉得有些重,但是也是一种优化方式。设计模式适合使用在大的业务流程和场景中使用,针对代码块中的if-else逻辑优化不推荐使用。
常用的设计模式有:

  • 策略模式
  • 模板方法
  • 工厂模式
  • 单例模式

还是以上面的营销拉新活动为例来说明如何使用。

使用技巧一:工厂模式 抽象类

  • 定义抽象业务接口

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; }}复制代码

  • 业务入口处根据不同渠道执行不同的发放逻辑

@PostMapping("/reward2")public void reward2(String userId, String source) { AwardAbstract instance = AwardFactory.getAwardInstance(source); if (null != instance) { instance.award(userId); }}复制代码

使用技巧二:策略模式 模板方法 工厂模式 单例模式

还是以营销拉新为业务场景来说明,这个业务流程再增加一些复杂度,比如发放奖励之前要进行 身份验证、风控验证 等一些列的校验,此时你的业务流程该如何实现更清晰简洁呢!

  • 定义业务策略接口

/** 策略业务接口 */public interface AwardStrategy { /** * 奖励发放接口 */ Map<String, Boolean> awardStrategy(String userId); /** * 获取策略标识,即不同渠道的来源标识 */ String getSource();}复制代码

  • 定义奖励发放模板流程

public abstract class BaseAwardTemplate { private static final Logger log = LoggerFactory.getLogger(BaseAwardTemplate.class); //奖励发放模板方法 public Boolean awardTemplate(String userId) { this.authentication(userId); this.risk(userId); return this.awardRecord(userId); } //身份验证 protected void authentication(String userId) { log.info("{} 执行身份验证!", userId); } //风控 protected void risk(String userId) { log.info("{} 执行风控校验!", userId); } //执行奖励发放 protected abstract Boolean awardRecord(String userId);}复制代码

  • 实现不同渠道的奖励业务

@Slf4j@Servicepublic 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; }}@Slf4j@Servicepublic 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; }}复制代码

  • 定义工厂方法,对外统一暴露业务调用入口

@Componentpublic class AwardStrategyFactory implements ApplicationContextAware { private final static Map<String, AwardStrategy> MAP = new HashMap<>(); @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map<String, AwardStrategy> beanTypeMap = applicationContext.getBeansOfType(AwardStrategy.class); beanTypeMap.values().forEach(strategyObj -> MAP.put(strategyObj.getSource(), strategyObj)); } /** * 对外统一暴露的工厂方法 */ public Boolean getAwardResult(String userId, String source) { AwardStrategy strategy = MAP.get(source); if (Objects.isNull(strategy)) { throw new RuntimeException("渠道异常!"); } return strategy.awardStrategy(userId); } /** * 静态内部类创建单例工厂对象 */ private static class CreateFactorySingleton { private static 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); }}复制代码

假如发起请求: POST http://localhost:8080/activity/reward3?userId=fei&source=wx

2022-02-20 12:23:27.716 INFO 20769 --- [nio-8080-exec-1] c.a.c.e.o.c.p.s.BaseAwardTemplate : fei 执行身份验证!2022-02-20 12:23:27.719 INFO 20769 --- [nio-8080-exec-1] c.a.c.e.o.c.p.s.BaseAwardTemplate : fei 执行风控校验!2022-02-20 12:23:27.719 INFO 20769 --- [nio-8080-exec-1] a.c.e.o.c.p.s.WeChatAwardStrategyService : 微信渠道用户fei奖励100元红包!复制代码

其他技巧

  • 使用三目运算符
  • 相同业务逻辑提取复用

相关新闻

联系我们
联系我们
公众号
公众号
在线咨询
分享本页
返回顶部