Java使用不同方式优雅拆分业务逻辑
如何处理复杂的业务逻辑
在实际的业务开发当中,经常会遇到复杂的业务逻辑,可能实现出来的代码并没有什么问题,但是代码的可读性很差。
那么在实际开发中如何避免大面积的 if-else 代码块的问题?
补充说明一点,不是说 if-else 不好,而是多层嵌套的 if-else 导致代码可读性差、维护成本高等问题。
拆分逻辑单元最小化
把业务代码拆分成多段逻辑,提取通用的代码块,核心思想就是逻辑单元最小化,然后合并逻辑单元。
逻辑封装到枚举类中
利用“枚举”来优化复杂的多重业务判断代码,将业务中的逻辑封装到枚举类中。
public enum TimeEnum {
AM("am", "上午") {
@Override
public void setCity(TestCodeData data) {
}
},
PM("pm", "下午") {
@Override
public void setCity(TestCodeData data) {
}
},
DAY("day", "全天") {
@Override
public void setCity(TestCodeData data) {
}
};
public abstract void setCity(TestCodeData data);
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;
}
}
// 使用方式
TimeEnum.AM.setCity(data); // 固定code:am
TimeEnum.valueOf("am").setCity(data); // 动态传入code:am
利用函数式接口优化
业务场景描述:比如让你做一个简单的营销拉新活动,这个活动投放到不同的渠道,不同渠道过来的用户奖励不一样。
假设在头条、微信等渠道都投放了该活动。此时你的代码可能会写出如下形式:
@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<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 自定义函数式接口来满足你的业务需求,使用原理和本例并无差别。
工厂模式+抽象类
还是以上面的营销拉新活动为例来说明如何使用。
定义抽象业务接口:
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("/reward")
public void reward(String userId, String source) {
AwardAbstract instance = AwardFactory.getAwardInstance(source);
if (null != instance) {
instance.award(userId);
}
}
策略模式
还是以营销拉新为业务场景来说明
定义策略业务接口:
/**
* 策略业务接口
*/
public interface AwardStrategy {
/**
* 奖励发放接口
*/
Boolean award(String userId);
}
定义具体业务实现类:
@Service("toutiao")
public class TouTiaoAwardService implements AwardStrategy {
/**
* 头条渠道发放奖励业务
*/
@Override
public Boolean award(String userId) {
log.info("头条渠道用户{}奖励50元红包!", userId);
return Boolean.TRUE;
}
}
@Service("wx")
public class WeChatAwardService implements AwardStrategy {
/**
* 微信渠道发放奖励业务
*/
@Override
public Boolean award(String userId) {
log.info("微信渠道用户{}奖励100元红包!", userId);
return Boolean.TRUE;
}
}
业务入口根据不同渠道执行不同的发放逻辑:
@Autowired
private ApplicationContext applicationContext;
@GetMapping("award")
public void award(@RequestParam("type") String type, @RequestParam("userId") String userId) {
AwardStrategy service = applicationContext.getBean(type, AwardStrategy.class);
service.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
@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<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("/reward")
public void reward(String userId, String source) {
AwardStrategyFactory.getInstance().getAwardResult(userId, source);
}
}
总结寄语
设计模式对于 if-else 的优化,个人觉得有些重,但是也是一种优化方式。
设计模式适合使用在大的业务流程和场景中使用,针对代码块中的 if-else 逻辑优化不推荐使用。
不论使用那种技巧,首先是我们在业务代码开发过程中一定要多思考,将复杂的业务逻辑能通过简洁的代码表现出来,这才是你的核心能力之一,而不是一个 curd boy。
Java使用不同方式优雅拆分业务逻辑的更多相关文章
- SpringBoot自定义异常,优雅解决业务逻辑中的错误
概要 你是不是在为业务逻辑中出现的异常弄的焦头烂额,常常在后台报错,前端却无法提示错误内容,导致用户体验极差?比如下单失败,前端只能提示下单失败,但是却不知道为什么失败,是库存不足,还是余额不足,亦或 ...
- 减少存储过程封装业务逻辑-web开发与传统软件开发的思维模式不同
本篇文章讨论并不是:不要使用存储过程,因为有些事情还是要存储过程来完成,不可能不用.而是关于:"业务逻辑是不是要封装在存储过程中实现,这样子php.java等就是调用存储过程". ...
- Java Swing项目专栏之项目业务流程与业务逻辑
Java Swing项目专栏 项目前言 这个超市管理项目是从八月初开始的,原以为像我这样的小菜比是完全掌控不了这样的项目的.原因是因为大一大二还是没怎么好好学自己的专业课,这次项目做完,我给自己建立了 ...
- java中异常处理机制 throw抛出自定义业务逻辑异常 throws继续抛出 catch捕获后会自动继续抛向调用方法
package com.swift; public class Exception_TestC { public static void main(String[] args) { /* * 第5题: ...
- java秀发入门到优雅秃头路线导航【教学视频+博客+书籍整理】
目录 一.Java基础 二.关于JavaWeb基础 三.关于数据库 四.关于ssm框架 五.关于数据结构与算法 六.关于开发工具idea 七.关于项目管理工具Mawen.Git.SVN.Gradle. ...
- Java中如何更优雅的处理空值
经常看到项目中存在到处空值判断的情况,这些判断,会让人觉得摸不着头绪,它的出现很有可能和当前的业务逻辑并没有关系.但它会让你很头疼.有时候,更可怕的是系统因为这些空值的情况,会抛出空指针异常,导致业务 ...
- Java 技术栈中间件优雅停机方案设计与实现全景图
欢迎关注公众号:bin的技术小屋,阅读公众号原文 本系列 Netty 源码解析文章基于 4.1.56.Final 版本 本文概要 在上篇文章 我为 Netty 贡献源码 | 且看 Netty 如何应对 ...
- MyBatis知多少(6)表现层与业务逻辑层
表现层 表现层负责向最终用户展示应用程序的控制方式以及数据.它还要负责所有信息的布局和格式.今天,商业应用程序最流行的表现方式应该算是Web前端了,它使用HTML和JavaScript并通 过Web浏 ...
- 如何利用动态URL提升SEO及处理业务逻辑
如果你正在建设一个新网站或者对现有网站重新设计,我们认为应该将网站的 URL 转换为用户友好的 URL,或搜索引擎友好的 URL,这类 URL 也称为语义 URL(Semantic URL).哪些UR ...
- RxJava系列番外篇:一个RxJava解决复杂业务逻辑的案例
之前写过一系列RxJava的文章,也承诺过会尽快有RxJava2的介绍.无奈实际项目中还未真正的使用RxJava2,不敢妄动笔墨.所以这次还是给大家分享一个使用RxJava1解决问题的案例,希望对大家 ...
随机推荐
- 什么是NASM
什么是NASM NASM是一个汇编器的名称,全称是Netwide Assembler,支持x86与x64架构的CPU(注意不支持ARM架构). 我们知道,在x86和x64架构上有多个操作系统,比较流行 ...
- DNS(5) -- 主从dns
目录 1. DNS主从服务器 1.1 主从服务器概述 1.2 主从DNS实现 1.2.1 主从DNS实现要点 1.2.2 主DNS服务器配置 1.2.3 从DNS服务器配置 1.2.4 测试主从同步 ...
- Java 获取两个时间的时间差
前言 在平时的工作中,难免会遇到获取两个时间相差多少天.小时.分钟.秒.毫秒,现在我将自己获取的方法总结如下: 一.导入需要的依赖 <dependency> <groupId> ...
- C 语言编程 — 函数
目录 文章目录 目录 前文列表 函数 函数的声明 函数的定义 函数的形参与实参 值传递 引用传递 可变长形参列表 函数的调用 函数的指针 回调函数 递归函数 数的阶乘 斐波那契数列 构造函数(Cons ...
- [渗透测试] HTB_Surveillance WriteUp [上]
靶机:Surveillance (from Hack The Box) 工具:Kali Linux 目标:拿到user和root的一串32位hex字符串 ## 配置hosts 环境启动后,要设置 ...
- .NET桌面程序混合开发之三:WebView2与JS的深度应用
在 WebView2 控件中使用 JavaScript 根据需求自由扩展原生应用的能力.本文探讨如何在 WebView2 中使用 JavaScript,并列举如何使用高级 WebView2 特性和功能 ...
- LlamaFS自组织文件管理器
LlamaFS是一个自组织文件管理器.它可以基于文件内容和修改时间等属性自动重命名和组织您的文件.它能让你不把时间花在对文件的复制.粘贴.重命名.拷贝.排序等简单操作上.有幸在Github上看到Lla ...
- nginx分流配置
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; eve ...
- kubernetes使用metrics-server进行资源监控
kubernetes资源监控 1. 查看集群资源状况 ·k8s集群的master节点一般不会跑业务容器· kubectl get cs #查看master资源状态 kubectl get node # ...
- 前端项目npm install安装报错:code ERESOLVE ERESOLVE could not resolve
背景:使用npm 安装依赖的时候,发现报了如下的错误: npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tr ...