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

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

 

posted @ 2018-12-19 21:42  harrychinese  阅读(626)  评论(0编辑  收藏  举报