========================================
原始代码
========================================
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. Spark SQL中列转行(UNPIVOT)的两种方法

    行列之间的互相转换是ETL中的常见需求,在Spark SQL中,行转列有内建的PIVOT函数可用,没什么特别之处.而列转行要稍微麻烦点.本文整理了2种可行的列转行方法,供参考. 本文链接:https: ...

  2. 万物皆有始有终: Hawk5即日起停止升级迭代

    从即日起,Hawk将停止升级工作,其版本号将停留在5. https://github.com/ferventdesert/Hawk Hawk已经开发和维护6年时间了,它曾经承载了开发者很多的期待.背后 ...

  3. Enterprise Architect 时序图

    添加时序图 1,在类图下面新建包 添加sequence时序图 点击流程控制,可以打开流程控制设计界面 我选择的是Lifeline线,你可以选择都差不多. 点击其中一条liftline连到其他上面 双击 ...

  4. adb常用命令(golang版)及输入中文

    package main import ( "crypto/md5" "fmt" "image/png" "io/ioutil&q ...

  5. 文本分类实战(十)—— BERT 预训练模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  6. Spring Security(三十四):10.4 Jackson Support

    Spring Security has added Jackson Support for persisting Spring Security related classes. This can i ...

  7. Linux启动时间优化-内核和用户空间启动优化实践

    关键词:initcall.bootgraph.py.bootchartd.pybootchart等. 启动时间的优化,分为两大部分,分别是内核部分和用户空间两大部分. 从内核timestamp 0.0 ...

  8. Linux内存管理 (16)内存规整

    专题:Linux内存管理专题 关键词:内存规整.页面迁移.pageblock.MIGRATE_TYPES. 内存碎片的产生:伙伴系统以页为单位进行管理,经过大量申请释放,造成大量离散且不连续的页面.这 ...

  9. sass的使用

    1.声明变量-全局声明-局部声明 中划线或下划线两种用法相互兼容  $nav-color: #F90; $highlight-border: 1px solid $nav-color;  nav{ $ ...

  10. opentack-openstack组件及功能(1)

    一. OpenStack各组件间的关系 图22.1 OpenStack各组件间的关系 1.基础管理服务包含Keystone,Glance,Nova,Neutron,Horizon五个服务 (1)Key ...