========================================
原始代码
========================================
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)); } }