Java 函数式编程
声明式编程(Declarative Programming)是一种编程风格,我们说明想要完成什么(what),而不是指明怎么做(how)。 --《On Java》
与之相对的是命令式编程(Imperative Programming),后者关注具体的步骤和操作顺序。声明式编程通常更简洁、更易于理解和维护。
Java 8 引入了新特性:
● 函数式编程:包括Lambda 表达式、函数式接口、方法引用等
● 接口默认方法和静态方法:增强接口的功能,支持函数式编程。
Stream API、Optional 提高了代码的简洁性和可读性,开发中用的比较多。
但是 Javaer 常常忽略了函数式编程带来的影响或者它的作用,也就是代码风格的改变。这里抛砖引玉,不讲原理,直讲开发的中的改变。Java 8 提供了一些常用的内置函数式接口:
● Function<T, R>:接受一个参数,返回一个结果。
● Predicate:接受一个参数,返回一个布尔值。
● Supplier:不接受参数,返回一个结果。
● Consumer:接受一个参数,不返回结果。
一、命令式编程
在web编程里,良好的编程习惯式不能把底层的实体类(DO)直接返回给请求接口,常常涉及到一些类型转换。
JDK 8 之前类型转换一般有几种做法:
● 类型直接写在业务代码里
● 把类型转换代码放到 private 方法里
● 把类型转换代码放到一个类型转换类中
这样做的缺点:
● 可读性差:代码冗长,不适合给代码审核人员/其他开发快速了解业务逻辑
public void saveTask(WarningTaskAddCriteria req) {// 存储预警任务基本信息SysWarningTaskEntity taskEntity = new SysWarningTaskEntity().setWarningEventId(req.getTaskEventInfo().getWarningEventId()).setWarningEventName(req.getTaskEventInfo().getWarningEventName()).setTaskStatus(TaskStatus.SAVED.getCode()).setAddAttributes();BeanUtils.copyProperties(req,taskEntity);final int taskInsertAffectedRows = taskMapper.insert(taskEntity);// 省略其他操作
}
二、声明式(函数式)编程
函数式接口可以作为参数、返回结果,也可以组合使用。
举个简单的例子 类型转化类 。个人习惯是封装一层,团队统一代码风格。
类型转化类:
import java.util.function.Function;public class TypeConverter {public static <T, R> R convert(T t, Function<T, R> function) {if (t == null || function == null) {throw new IllegalArgumentException("Arguments cannot be null");}return function.apply(t);}
}
改造后的代码:
public void saveTask(WarningTaskAddCriteria req) {// 存储预警任务基本信息SysWarningTaskEntity taskEntity = TypeConverter.convert(req.getTaskEventInfo(), info -> {SysWarningTaskEntity entity = new SysWarningTaskEntity().setWarningEventId(info.getWarningEventId()).setWarningEventName(info.getWarningEventName()).setTaskStatus(TaskStatus.SAVED.getCode()).setAddAttributes();SpringBeanUtils.copyProperties(req, entity);return entity;});final int taskInsertAffectedRows = taskMapper.insert(taskEntity);// 省略其他操作
}// 更极端一些
public void saveTask(WarningTaskAddCriteria req) {// 存储预警任务基本信息final int taskInsertAffectedRows = taskMapper.insert(TypeConverter.convert(req.getTaskEventInfo(), info -> {SysWarningTaskEntity entity = new SysWarningTaskEntity().setWarningEventId(info.getWarningEventId()).setWarningEventName(info.getWarningEventName()).setTaskStatus(TaskStatus.SAVED.getCode()).setAddAttributes();SpringBeanUtils.copyProperties(req, entity);return entity;}));// 省略其他操作
}
改造后的优点:
● 可读性更强:虽然代码看起来只是放到不同的地方,但是 其他开发人员看到 TypeConverter.convert就会明白这个是要做什么(what) 而不是 怎么做(how)。
● 更易于维护:在实际工作中见到过太多情况,前面声明一个类,然后后面的代码又对这个对象进行更改。一般来讲 final 的对象更加安全可靠。
三、风格改变会有什么影响吗?
3.1 事务是否会有影响?
答案是不会有影响。
@Transactional
public void saveTask(WarningTaskAddCriteria req) {SysWarningTaskEntity taskEntity = new SysWarningTaskEntity().setWarningEventId(req.getTaskEventInfo().getWarningEventId()).setWarningEventName(req.getTaskEventInfo().getWarningEventName()).setTaskStatus(TaskStatus.SAVED.getCode()).setAddAttributes();BeanUtils.copyProperties(req, taskEntity);// 这里仅仅是为了演示,没必要这么写Consumer<SysWarningTaskEntity> save = entity -> {taskMapper.insert(entity);};save.accept(taskEntity);// 强制抛出异常测试回滚throw new RuntimeException("Test rollback");
}