声明式编程(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");
}
四、函数式例子
4.1 链式函数调用
public JobDetails findJobById(String jobId) {
// 查询任务基础信息
Function<String,JobDetails> queryJobBaseInfo = id -> {
JobDetails jobDetails = new JobDetails();
final SysWarningJobEntity jobBaseInfo = jobMapper.selectById(id);
jobDetails.setJobGroupId(jobBaseInfo.getJobGroup());
BeanUtils.copyProperties(jobBaseInfo,jobDetails);
return jobDetails;
};
// 查询任务参数信息
Function<JobDetails, JobDetails> queryJobParams = jobDetails -> {
LambdaQueryWrapper<SysWarningJobParamEntity> wrapper = WrapperUtil.createQueryWrapper(SysWarningJobParamEntity.class)
.eq(SysWarningJobParamEntity::getJobId,jobDetails.getId());
final List<WarningJobParamDTO> jobParams = jobParamMapper.selectList(wrapper).stream().map(paramEntity -> {
WarningJobParamDTO jobParamDTO = new WarningJobParamDTO();
BeanUtils.copyProperties(paramEntity, jobParamDTO);
return jobParamDTO;
}).collect(Collectors.toList());
jobDetails.setJobParams(jobParams);
return jobDetails;
};
// 查询模板变量信息
Function<JobDetails, JobDetails> queryTemplateVariables = jobDetails -> {
LambdaQueryWrapper<SysWarningJobPlaceholderConfigEntity> wrapper = WrapperUtil.createQueryWrapper(SysWarningJobPlaceholderConfigEntity.class)
.eq(SysWarningJobPlaceholderConfigEntity::getJobId, jobDetails.getId());
final List<JobTemplateVariable> jobTemplateVariables = placeholderConfigMapper.selectList(wrapper).stream().map(entity -> {
JobTemplateVariable variable = new JobTemplateVariable();
variable.setVariableCode(entity.getPlaceholderCode());
variable.setVariableName(entity.getPlaceholderName());
variable.setVariableType(entity.getType());
return variable;
}).collect(Collectors.toList());
jobDetails.setJobTemplateVariables(jobTemplateVariables);
return jobDetails;
};
// 使用链式调用
return queryJobBaseInfo
.andThen(queryJobParams)
.andThen(queryTemplateVariables)
.apply(jobId);
}
public void delete(String jobId) {
// 删除任务
Consumer<String> deleteJob = id -> {
SysWarningJobEntity jobEntity = new SysWarningJobEntity().setDeleteAttributesWithPrimaryKey(id);
jobMapper.updateById(jobEntity);
};
//删除 任务参数
Consumer<String> deleteJobParam = id -> {
LambdaUpdateWrapper<SysWarningJobParamEntity> wrapper = WrapperUtil.createDeleteWrapper(SysWarningJobParamEntity.class)
.eq(SysWarningJobParamEntity::getJobId, id);
jobParamMapper.update(wrapper);
};
//删除 任务模版变量
Consumer<String> deleteJobTemplateVar = id -> {
LambdaUpdateWrapper<SysWarningJobPlaceholderConfigEntity> wrapper = WrapperUtil.createDeleteWrapper(SysWarningJobPlaceholderConfigEntity.class)
.eq(SysWarningJobPlaceholderConfigEntity::getJobId, id);
placeholderConfigMapper.update(wrapper);
};
deleteJob.andThen(deleteJobParam).andThen(deleteJobTemplateVar).accept(jobId);
}