========================================
原始代码
========================================
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. SQLServer之创建事务未提交读

    未提交读注意事项 使用 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 指定会话的锁定级别. 一次只能设置一个隔离级别选项,而且设置的选项将一直对那个 ...

  2. mysql_报错1418

    报错如下: 1418 - This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration a ...

  3. 数据可视化的开源方案: Superset vs Redash vs Metabase (二)

    在上篇结尾处我提到“如果现在让我重新选择,我会使用哪个可视化工具?”我的答案是 Redash,原因主要不是功能层面,而是技术层面.本篇就从项目关注度与活跃度,项目的技术架构,源代码的规模与质量,这三个 ...

  4. gulp配置(编译压缩转码自动刷新注释全)

    参考自:http://www.sheyilin.com/2016/02/gulp_introduce/ 在原先基础上增加了less编译 es6转码资源地图等,修改了一部分的热刷新. gulpfile. ...

  5. canvas如何自适应屏幕大小

    可以用JS监控屏幕大小,然后调整Canvas的大小.在代码中加入JS $(window).resize(resizeCanvas);  function resizeCanvas() {        ...

  6. bat——批量删除文件文件夹

    bat批处理,在工作中会带来很多便利. 例如:想删除多个文件夹内的文件夹“Quality”及其子文件 同时删除所有Cyc*文件夹内的所有R00*.tif文件 则可如下操作 先建立父bat文件run_d ...

  7. EF Core 遇到“可能会导致循环或多重级联路径”

    在ef core中你可能会设计这样一个实体: public class Customer : Entity,IMustHaveTenant, IHasCreationTime { public Cus ...

  8. mapreduce map 的个数

    在map阶段读取数据前,FileInputFormat会将输入文件分割成split.split的个数决定了map的个数.影响map个数(split个数)的主要因素有: 1) 文件的大小.当块(dfs. ...

  9. 阿里云对象存储OSS与文件存储NAS的区别

    一.简介 应用场景:选择一款存储产品,面向文档数据的存取,不会涉及到数据处理. 产品选型主要从OSS和NAS中选择一款,满足文档存储的需求. 二.NAS优缺点 NAS 是一种采用直接与网络介质相连的特 ...

  10. ubuntu only enable left click

    xmodmap -e "pointer = 1 0 0 0 0 0 0 0 0 0"