《Java 8 in Action》Chapter 2:通过行为参数化传递代码
你将了解行为参数化,这是Java 8非常依赖的一种软件开发模式,也是引入 Lambda表达式的主要原因。行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式。一言以蔽之,它意味 着拿出一个代码块,把它准备好却不去执行它。这个代码块以后可以被你程序的其他部分调用。本章通过筛选苹果这个实际需求来一步步引出Lambda表达式,同时我也会把代码贴出来,读完你会看到代码是如何一步一步的向Lambda转化。多代码来袭,保护我方ADC!!
代码演化
1.实习生版本
package com.lujiahao.learnjava8.chapter2;
import java.util.ArrayList;
import java.util.List;
/**
* 筛选绿色苹果
* @author lujiahao
* @date 2019-02-19 18:28
*/
public class FilterAppleV0 {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
List<Apple> greenAppleList = new ArrayList<>();
for (Apple apple : appleList) {
if ("green".equals(apple.getColor())) {
greenAppleList.add(apple);
}
}
System.out.println("原集合:" + appleList);
System.out.println("绿苹果集合:" + greenAppleList);
}
}
这种之所以称之为实习生版本,是因为此种写法比较初级,所有代码在一个方法中实现。没有进行方法的抽取,不符合面向对象的理念,希望大家在编码工作时避免这种写法。
2.方法抽取版本
package com.lujiahao.learnjava8.chapter2;
import java.util.ArrayList;
import java.util.List;
/**
* 筛选绿色苹果
* @author lujiahao
* @date 2019-02-19 18:30
*/
public class FilterAppleV1 {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
System.out.println("原集合:" + appleList);
List<Apple> filterGreenApples = filterGreenApples(appleList);
System.out.println("绿苹果集合:" + filterGreenApples);
}
/**
* 筛选绿色苹果
* @param appleList
* @return
*/
public static List<Apple> filterGreenApples(List<Apple> appleList) {
List<Apple> resultList = new ArrayList<>();
for (Apple apple : appleList) {
if ("green".equals(apple.getColor())) {
resultList.add(apple);
}
}
return resultList;
}
}
此版本对筛选绿色苹果的方法进行了简单的抽取,相较于上个版本有了很大的提升。然而,如果需求方改变想法,想筛选红色的苹果。复制filterGreenApples() 方法并将其中的绿色筛选条件改为红色,确实可以实现。但是,这样有太多重复的模板代码,不是良好的编码规范。因此,我们将筛选条件颜色进一步抽象化。
3.筛选条件作为参数传入
package com.lujiahao.learnjava8.chapter2;
import java.util.ArrayList;
import java.util.List;
/**
* 需要判断的属性作为参数传入
* @author lujiahao
* @date 2019-02-19 18:30
*/
public class FilterAppleV2 {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
System.out.println("原集合:" + appleList);
List<Apple> filterGreenApples = filterApples(appleList, "green");
System.out.println("筛选绿色苹果:" + filterGreenApples);
List<Apple> filterRedApples = filterApples(appleList, "red");
System.out.println("筛选红色苹果:" + filterRedApples);
}
/**
* 筛选特定颜色苹果
* @param appleList
* @return
*/
public static List<Apple> filterApples(List<Apple> appleList, String color) {
List<Apple> resultList = new ArrayList<>();
for (Apple apple : appleList) {
if (color.equals(apple.getColor())) {
resultList.add(apple);
}
}
return resultList;
}
}
满足了颜色的筛选条件,然而需求方又灵光一闪,筛选大于150克的苹果。无论是复制filterApples() 方法,还是增加重量作为参数传入,都是不推荐的编码习惯。第一种方法复制了大部分的代码来实现遍历,它打破了DRY(Don’t Repeat Yourself)的软件工程原则;第二种方法并不能考虑到所有情况,并且每次修改都对原有代码产生了影响,无法做到修改对外封闭的原则。
4.行为参数化
package com.lujiahao.learnjava8.chapter2;
import java.util.ArrayList;
import java.util.List;
/**
* 行为参数化
* @author lujiahao
* @date 2019-02-19 18:30
*/
public class FilterAppleV3 {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
System.out.println("原集合:" + appleList);
List<Apple> filterGreenApples = filterApples(appleList, new AppleGreenColorPredicate());
System.out.println("筛选绿色苹果:" + filterGreenApples);
List<Apple> filterHeavyApples = filterApples(appleList, new AppleHeavyWeightPredicate());
System.out.println("筛选重量大于150苹果:" + filterHeavyApples);
}
/**
* 筛选绿色苹果
* @param appleList
* @return
*/
public static List<Apple> filterApples(List<Apple> appleList, ApplePredicate predicate) {
List<Apple> resultList = new ArrayList<>();
for (Apple apple : appleList) {
// 谓词对象封装了条件
if (predicate.filter(apple)) {
resultList.add(apple);
}
}
return resultList;
}
public interface ApplePredicate {
boolean filter(Apple apple);
}
public static class AppleHeavyWeightPredicate implements ApplePredicate {
@Override
public boolean filter(Apple apple) {
return apple.getWeight() > 150;
}
}
public static class AppleGreenColorPredicate implements ApplePredicate {
@Override
public boolean filter(Apple apple) {
return "green".equals(apple.getColor());
}
}
}
我们对苹果的所有属性进行更高一个层次的抽象建模,通过定义ApplePredicate 接口,AppleHeavyWeightPredicate 和 AppleGreenColorPredicate 分别实现该接口来达到进行不同的筛选功能。客户端调用中创建不同的实现类,对于filterApple() 方法而言,是传入了不同的行为,即行为参数化。行为参数化:让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为。
其原理如下图所示:
5.匿名内部类
package com.lujiahao.learnjava8.chapter2;
import java.util.ArrayList;
import java.util.List;
/**
* 使用匿名类
* @author lujiahao
* @date 2019-02-19 18:30
*/
public class FilterAppleV4 {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
System.out.println("原集合:" + appleList);
List<Apple> filterGreenApples = filterApples(appleList, new ApplePredicate() {
@Override
public boolean filter(Apple apple) {
return "green".equals(apple.getColor());
}
});
System.out.println("筛选绿色苹果:" + filterGreenApples);
List<Apple> filterHeavyApples = filterApples(appleList, new ApplePredicate() {
@Override
public boolean filter(Apple apple) {
return apple.getWeight() > 150;
}
});
System.out.println("筛选重量大于150苹果:" + filterHeavyApples);
}
/**
* 筛选绿色苹果
* @param appleList
* @return
*/
public static List<Apple> filterApples(List<Apple> appleList, ApplePredicate predicate) {
List<Apple> resultList = new ArrayList<>();
for (Apple apple : appleList) {
// 谓词对象封装了条件
if (predicate.filter(apple)) {
resultList.add(apple);
}
}
return resultList;
}
public interface ApplePredicate {
boolean filter(Apple apple);
}
}
当每次有新的查询需求提出,都要新建一个实现类,随着条件越来越多,实现类的数量也在急剧上升。此时,通过使用匿名内部类的方式,来减少实现类过多的模板代码。然而,匿名内部类并非完美,第一,它往往很笨重,因为它占用了很多空间;第二,很多程序员觉得它用起来很让人费解。
6.使用 Lambda 表达式
package com.lujiahao.learnjava8.chapter2;
import java.util.ArrayList;
import java.util.List;
/**
* 使用Lambda表达式
* @author lujiahao
* @date 2019-02-19 18:30
*/
public class FilterAppleV5 {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
System.out.println("原集合:" + appleList);
List<Apple> filterGreenApples = filterApples(appleList, (Apple apple) -> "green".equals(apple.getColor()));
System.out.println("筛选绿色苹果:" + filterGreenApples);
List<Apple> filterHeavyApples = filterApples(appleList, (Apple apple) -> apple.getWeight() > 150);
System.out.println("筛选重量大于150苹果:" + filterHeavyApples);
}
/**
* 筛选绿色苹果
* @param appleList
* @return
*/
public static List<Apple> filterApples(List<Apple> appleList, ApplePredicate predicate) {
List<Apple> resultList = new ArrayList<>();
for (Apple apple : appleList) {
// 谓词对象封装了条件
if (predicate.filter(apple)) {
resultList.add(apple);
}
}
return resultList;
}
public interface ApplePredicate {
boolean filter(Apple apple);
}
}
不得不承认这代码看上去比先前干净很多,而且它看起来更像是在陈述问题本身,更加通俗易懂。
7.List 类型抽象化
package com.lujiahao.learnjava8.chapter2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.*;
/**
* List类型抽象话
*
* @author lujiahao
* @date 2019-02-19 18:30
*/
public class FilterAppleV6 {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
System.out.println("原集合:" + appleList);
List<Apple> filterGreenApples = filter(appleList, (Apple apple) -> "green".equals(apple.getColor()));
System.out.println("筛选绿色苹果:" + filterGreenApples);
System.out.println("=============================================");
List<Integer> numberList = Arrays.asList(1, 2, 3);
System.out.println("原集合:" + numberList);
List<Integer> numbers = filter(numberList, (Integer i) -> i % 2 == 0);
System.out.println("能被2整除的数:" + numbers);
}
/**
* 筛选绿色苹果
*/
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> resultList = new ArrayList<>();
for (T t : list) {
// 谓词对象封装了条件
if (predicate.filter(t)) {
resultList.add(t);
}
}
return resultList;
}
public interface Predicate<T> {
boolean filter(T t);
}
}
在通往抽象的路上,我们还可以更进一步。目前,filterApples方法还只适用于Apple。还可以将List类型抽象化,从而支持所有类型。
8.演化小结
这一路演化中我们可以看出代码是如何一步一步转化的更加简洁更加优雅,对此我们进行总结:
实例
1.用 Comparator 排序
package com.lujiahao.learnjava8.chapter2;
import java.util.Comparator;
import java.util.List;
/**
* 用 Comparator 排序
* @author lujiahao
* @date 2019-03-02 18:34
*/
public class ComparatorDemo {
public static void main(String[] args) {
List<Apple> appleList = DataUtil.generateApples();
System.out.println("原集合:" + appleList);
appleList.sort(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getWeight().compareTo(o2.getWeight());
}
});
System.out.println("按重量升序:" + appleList);
appleList.sort((Apple a1, Apple a2) -> a1.getColor().compareTo(a2.getColor()));
System.out.println("按颜色字典排序:" + appleList);
}
}
2.用 Runnable 执行代码块
package com.lujiahao.learnjava8.chapter2;
/**
* 用 Runnable 执行代码块
* @author lujiahao
* @date 2019-03-02 18:42
*/
public class RunnableDemo {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello Java 8!");
}
});
t.start();
Thread t1 = new Thread(() -> System.out.println("Hello Lambda!"));
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.GUI 事件处理
Button button = new Button(“Send”);
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
lable.setText(“Send!!”);
}
}
button.setOnAction((ActionEvent event) -> lable.setText(“Send!!”));
小猿之前搞安卓开发的,各种控件的监听都是这个样子,想想以前各种代码啊啊啊~~~
总结
以下是你应从本章中学到的关键概念。
- 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
- 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。
- 传递代码,就是将新行为作为参数传递给方法。但在Java 8之前这实现起来很啰嗦。为接口声明许多只用一次的实体类而造成的啰嗦代码,在Java 8之前可以用匿名类来减少。
- Java API包含很多可以用不同行为进行参数化的方法,包括排序、线程和GUI处理。
资源获取
公众号回复 : Java8 即可获取《Java 8 in Action》中英文版!
Tips
- 欢迎收藏和转发,感谢你的支持!(๑•̀ㅂ•́)و✧
- 欢迎关注我的公众号:庄里程序猿,读书笔记教程资源第一时间获得!
《Java 8 in Action》Chapter 2:通过行为参数化传递代码的更多相关文章
- Java8 in action(1) 通过行为参数化传递代码--lambda代替策略模式
[TOC] 猪脚:以下内容参考<Java 8 in Action> 需求 果农需要筛选苹果,可能想要绿色的,也可能想要红色的,可能想要大苹果(>150g),也可能需要红的大苹果.基于 ...
- Java通过行为参数化传递代码
在软件工程中,一个众所周知的问题就是,不管做什么,用户的需求肯定会变.如何应对这样不断变化的需求?理想的状态下,应该把的工作量降到最少.此外,类似的新功能实现起来还应该很简单,而且易于长期维护.行为参 ...
- 《Java 8 in Action》Chapter 1:为什么要关心Java 8
自1998年 JDK 1.0(Java 1.0) 发布以来,Java 已经受到了学生.项目经理和程序员等一大批活跃用户的欢迎.这一语言极富活力,不断被用在大大小小的项目里.从 Java 1.1(199 ...
- 《Java 8 in Action》Chapter 3:Lambda表达式
1. Lambda简介 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表. 匿名--我们说匿名,是因为 ...
- 《Java 8 in Action》Chapter 4:引入流
1. 流简介 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现).就现在来说,你可以把它们看成遍历数据集的高级迭代器.此外,流还可以透明地并行 ...
- 《Java 8 in Action》Chapter 5:使用流
流让你从外部迭代转向内部迭代,for循环显示迭代不用再写了,流内部管理对集合数据的迭代.这种处理数据的方式很有用,因为你让Stream API管理如何处理数据.这样Stream API就可以在背后进行 ...
- 《Java 8 in Action》Chapter 9:默认方法
传统上,Java程序的接口是将相关方法按照约定组合到一起的方式.实现接口的类必须为接口中定义的每个方法提供一个实现,或者从父类中继承它的实现. 但是,一旦类库的设计者需要更新接口,向其中加入新的方法, ...
- 《Java 8 in Action》Chapter 10:用Optional取代null
1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法.ALGOL W是第一批在堆上分配记录的类型语言之一.Hoare选择null引用这种方式,& ...
- 《Java 8 in Action》Chapter 11:CompletableFuture:组合式异步编程
某个网站的数据来自Facebook.Twitter和Google,这就需要网站与互联网上的多个Web服务通信.可是,你并不希望因为等待某些服务的响应,阻塞应用程序的运行,浪费数十亿宝贵的CPU时钟周期 ...
随机推荐
- spring mvc 拦截器的使用
Spring MVC 拦截器的使用 拦截器简介 Spring MVC 中的拦截器(Interceptor)类似于 Servler 中的过滤器(Filter).用于对处理器进行预处理和后处理.常用于日志 ...
- Mysql常用增删查改及入门(二)
常用:数据库常用就是DML:增删查改 1.增加数据: insert into 表名 values (值1,值2...); insert into 表名 (字段1,字段2) values (值1,值2) ...
- 个人永久性免费-Excel催化剂功能第51波-聚光灯功能,长宽工作表不看错位使用
Excel的聚光灯功能,笔者是有点看不上,也曾经写文吐槽过这些类似的功能的实用性,但现实可能真的很多小白很需要,大家Excel水平参差不齐,大量的不规范做表习惯,致使此功能使用场景仍然非常广阔.很怀疑 ...
- MyBatis 中 @Param 注解的四种使用场景,最后一种经常被人忽略!
有一些小伙伴觉得 MyBatis 只有方法中存在多个参数的时候,才需要添加 @Param 注解,其实这个理解是不准确的.即使 MyBatis 方法只有一个参数,也可能会用到 @Param 注解. 但是 ...
- 《VR入门系列教程》之5---应用方向
VR应用方向 面向消费者的虚拟现实才发展了几年,就出现了大量应用程序,虚拟现实抓住了人们对未来的渴望.开发者甚至想要把整个现实世界都做成虚拟现实,这些都是可以理解的. 但是,现在仍然没 ...
- layer设置maxWidth及maxHeight解决方案
layer介绍 layer是一款近年来备受青睐的web弹层组件,她具备全方位的解决方案,致力于服务各水平段的开发人员,您的页面会轻松地拥有丰富友好的操作体验.下载及使用访问官方网站. area属性 l ...
- visionpro和halcon这两款机器视觉软件区别
很多朋友会问到visionpro和halcon这两款机器视觉软件,到底学哪个好呢,今天重码网就给大家讲一讲: 首先比较下两者的优缺点: halcon: 提供的图像算法要比Visionpro多,也就是说 ...
- tomcat7之性能优化
一.Tomcat 7.0.X: 要了解tomcat的优化,我们先看看Tomcat的官方定义:The Apache Tomcat® software is an open source implemen ...
- JavaScript的面向对象原理之原型链
二.JavaScript的对象 为了能够清楚的解释这一切,我先从对象讲起.从其他面向对象语言(如Java)而来的人可能认为在JS里的对象也是由类来实例化出来的,并且是由属性和方法组成的. 实际上在JS ...
- 使用f12定位bug
为什么找到网站中的bug后还要去分析它到底是属于前端bug还是后端bug 三个原因: 1.在一些公司,一个系统可能是由前端团队和后端团队共同开发出来的,因此在分配bug的时候,不同模块的bug一般都会 ...