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

使用 functional interface 和 lambda 表达式来优化代码的更多相关文章

  1. Lambda表达式常用代码示例

    Lambda表达式常用代码示例 2017-10-24 目录 1 Lambda表达式是什么2 Lambda表达式语法3 函数式接口是什么  3.1 常用函数式接口4 Lambdas和Streams结合使 ...

  2. Java疯狂讲义笔记——Lambda表达式

    Java8新增的Lambda表达式 [特性]支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例. [组成部分]1,形参列表 ...

  3. Lambda表达式(一)入门认识篇

    Lambda表达式(一)入门认识篇 Lambda简介 Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极 ...

  4. 07_Java8新增的Lambda表达式

    [Lambda表达式概述] Lambda表达式支持将代码块作为方法参数,Lambda表达式允许将使用简洁的代码来创建只有一个抽象方法的接口的实例.(这种接口称为函数式接口) [入门实例] packag ...

  5. Java 8新特性探究(一) JEP126特性lambda表达式和默认方法

    Lambda语法 函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的java.la ...

  6. Java函数式编程和lambda表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...

  7. lambda表达式的应用例子和JavaSE 8特性

    在JavaSE 8 引入了lambda表达式,lambda表达式的引入带来的好处是:通过语法上的改进,减少开发人员需要编写和维护的代码数量.这个在下面使用和不使用lambda的对比中可以清晰看出来. ...

  8. <JAVA8新增内容>关于匿名内部集合和lambda表达式

    要想说清楚JAVA中的Lambda表达式,必须想讲一下匿名内部类来帮助理解本质. 一.匿名内部类 匿名内部类适合创建那种只需要一次使用的类,例如前面介绍命令模式时所需要的Command对象,匿名内部类 ...

  9. 对比讲解lambda表达式与传统接口函数实现方式

    在本号之前写过的一些文章中,笔者使用了lambda表达式语法,一些读者反映说代码看不懂.本以为java 13都已经出了,java 8中最重要特性lambda表达式大家应该都掌握了,实际上还是存在大量的 ...

随机推荐

  1. jenkins安装详细教程

    一.jenkins简介 jenkins是一个开源的软件项目,是基于java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能. 1.持续的软件版本 ...

  2. 索引-mysql

    什么是索引 索引的四大作用 (参考百度百科:百度百科-索引) 索引的优缺点 索引分类 什么地方需要建索引 索引优化 什么是索引? 宏观来说,索引是一种检索工具,目录也是一种检索工具,但是两者是有区是别 ...

  3. Jenkins+VS项目持续集成

    软件安装 安装包下载连接:https://jenkins.io/download/ 安装步奏:略 账户名:admin 密码:C:\Program Files (x86)\Jenkins\secrets ...

  4. China Tightens Recycling Import Rules

    China Tightens Recycling Import Rules We have all seen the pictures of cities in China with air poll ...

  5. java程序启动 环境属性的获取

    System.getProperties().list(System.out); 如果要获取某一个属性,例如常见的“操作系统” 则  System.getProperty("os.name& ...

  6. 部署Java和Tomcat

    Tomcat介绍 Tomcat服务器是一个免费的开放源代码的Web应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP网页的首选. Tomcat和Nginx.Apach ...

  7. 美团--Quake全链路压测平台

    原文:连接: https://tech.meituan.com/2018/09/27/quake-introduction.html 开源分布式监控Cat: https://github.com/di ...

  8. 控制结构(10): 指令序列(opcode)

    // 上一篇:管道(pipeline) // 下一篇:Continuation-passing_style(CPS) 发现问题 在一个正式项目的开发周期中,除了源代码版本控制外,还存在着项目的配置/编 ...

  9. jmeter学习记录--07--jmeter元件

    通过jmeter元件可以模拟负载.参数化.设置关联.设置检查点.设置集合点.控制场景运行.监控测试结果等. 1.逻辑控制器:比如foreach控制器,查询到了订单并要对每个订单进行出库操作,以订单号作 ...

  10. 题解 P1601 【A+B Problem(高精)】

    P1601 A+B Problem(高精) 题目描述 高精度加法,x相当于a+b problem,b不用考虑负数. 输入输出格式 输入格式: 分两行输入a,b<=10^500 输出格式: 输出只 ...