java实战技巧之if

网友投稿 220 2022-08-27


java实战技巧之if

目录前言技巧一:提取方法,拆分逻辑技巧二:分支逻辑提前return技巧三:枚举技巧四:函数式接口技巧五:设计模式使用技巧一:工厂模式+抽象类使用技巧二:策略模式+模板方法+工厂模式+单例模式其他技巧写在最后

前言

在实际的业务开发当中,经常会遇到复杂的业务逻辑,可能部分同学实现出来的代码并没有什么问题,但是代码的可读性很差。本篇文章主要总结一下自己在实际开发中如何避免大面积的 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.getEBIRQCLUndTime() == 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;

http:// }

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> 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 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 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

@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;

}

}

@Slf4j

@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 final static Map MAP = new HashMap<>();

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

Map 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元红包!

其他技巧

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

写在最后

不论使用那种技巧,首先是我们在业务代码开发过程中一定要多思考,将复杂的业务逻辑能通过简洁的代码表现出来,这才是你的核心能力之一,而不是一个 curd boy。与君共勉,共同进步!

Gitee 获取源码


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:python绘图库matplotlib:刻度线的方向调整, in, out, inoutpython绘图库matplotlib:画线的标志marker的设置——类型/(matplotlib绘图更改X轴刻度)
下一篇:Python数据可视化库pyecharts(Python实现数据可视化)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~