• 使用 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));
        }
    }   
  • 相关阅读:
    常用数据结构的应用场景
    数组与链表的对比
    [LeetCode 293] Flip Game
    [Leetcode] Palindrome Permutation 回文变换
    九大排序算法再总结
    query函数的可查询数据
    Column常用的参数
    sqlalchemy的常用字段
    sqlalchemy基本的增删改查
    sqlalchemy映射数据库
  • 原文地址:https://www.cnblogs.com/harrychinese/p/java_functional_interface_and_lambda.html
Copyright © 2020-2023  润新知