使用 functional interface 和 lambda 表达式来优化代码

时间:2024-12-01 22:05:37

========================================
原始代码
========================================
RoleService 类有删除角色和锁定角色两个函数.

@Service
public class RoleService { @Autowired
RoleDao roleDao; /**
* 删除指定的角色
* @param roleId
*/
public void deleteRole(Long roleId) {
Optional<Role> oldRole = roleDao.getRole(roleId);
if (oldRole.isPresent()) {
roleDao.delete(roleId); //1: 不同点
} else {
throw new BusinessException(String.format("Cannot find the role record for id [%d]", roleId));
}
} /**
* 锁定指定的角色
* @param roleId
*/
public void lockRole(Long roleId) {
Optional<Role> oldRole = roleDao.getRole(roleId);
if (oldRole.isPresent()) {
roleDao.lock(roleId) //2: 不同点
} else {
throw new BusinessException(String.format("Cannot find the role record for id [%d]", roleId));
}
}
}

问题分析:
可以看到上面这两个函数逻辑完全一致, 仅仅是最终调用的函数不同, 应该能优化, 一个思路是: 将框架部分封装为一个公用函数,  deleteRole() 和 lockRole() 函数调用该公共函数, 并将最终的 action 想法儿传进去.
如果是 C#, 因为有 delegate, 很容易做到; 如果是 Python的话, 那就更容易了, 直接将函数作为参数即可. 但 Java 不允许以函数作为参数, 处理起来要麻烦一些.

Java具体优化思路:虽然 Java 不允许以函数作为实参, 但是我们先将函数封装为类, 然后将类对象作为实参传进去.
具体实现方式有:
1. Java  8 之前,  利用 Runnable 或 Callable 接口作为公共函数的形参, 实参可以用真实的实现类, 也可以使用匿名类. 
2. Java 8 , 可以使用 java.util.function.Consumer 等接口作为公共函数的形参, 实参可以用真实的实现类, 也可以使用匿名类, 也可以使用 lambda 表达式.

显然最简洁的组合是: 使用 Java 8 中 java.util.function.Consumer 等接口作为公共函数的形参, 使用 lambda 做实参.

========================================
Java 8 内置的 Functional 接口
========================================
java.util.function 包中包含了一些常用的 Functional Interface, 而且都支持泛型. 所谓Functional Interface接口就是只有一个虚函数的接口. 主要的接口包括:
Consumer 接口, 该接口主要函数有一个形参, 没有返回值.
Function 接口, 该接口主要函数有一个形参, 可以有返回值.
Supplier 接口, 该接口主要函数没有形参, 但有返回值.
Predicate 接口, 该接口主要函数接受一个参数, 返回布尔型值.

正如上面所讲, Functional Interface接口就是只有一个虚函数的接口, 没什么特别之处, 我们也很容易自定义一个, 定义方式也和普通的Interface一样, 当然最好加上 @FunctionalInterface 注解, 这样如果不小心声明了多个虚函数, 编译时会报错.

Functional Interface 典型用法不是: 先声明一个接口, 然后在编写一个实现类.  而是: 用来声明函数的形参, 或者用来声明一个变量,  然后使用lambda表达式进行赋值. 

有了这一功能, Java 8 也具有一定的函数式编程特性.

========================================
重构后的代码
========================================

@Service
class RoleService2 {
@Autowired
RoleDao roleDao; /** 提取出的公共函数, 先判断指定角色是否存在, 若存在则继续执行某个动作, 若不存在直接抛出异常
* @param roleId
* @param action
*/
private void checkAndDo(Long roleId, java.util.function.Consumer<Long> action) {
Optional<Role> oldRole = roleDao.getRole(roleId);
if (oldRole.isPresent()) {
action.accept(roleId);
} else {
throw new BusinessException(String.format("Cannot find the role record for id [%d]", roleId));
}
} private void internalLock(Long roleId) {
roleDao.updateRoleState(roleId, RoleStateEnum.LOCKED);
} public void lockRole(Long roleId) {
checkAndDo(roleId, (rId) -> internalLock(rId));
} public void deleteRole(Long roleId) {
checkAndDo(roleId, (rId) -> roleDao.updateRoleState(rId, RoleStateEnum.DELETED));
}
}