本文源码

应对不断变化的需求

通过筛选苹果阐述通过行为参数传递代码

初试牛刀:筛选绿苹果

public static List<Apple> filterGreenApples(List<Apple> inventory){List<Apple> result = new ArrayList<>();for(Apple apple: inventory){if("green".equals(apple.getColor())){result.add(apple);}}return result;
}

要是农民想要筛选多种颜色:浅绿色、暗红色、黄色等,这种方法就应付不了了。一个良好的原则是在编写类似的代码之后,尝试将其抽象化。

再展身手:把颜色作为参数

public static List<Apple> filterApplesByColor(List<Apple> inventory, String color){List<Apple> result = new ArrayList<>();for(Apple apple: inventory){if(apple.getColor().equals(color)){result.add(apple);}}return result;
}

运用

List<Apple> greenApples = filterApplesByColor(inventory, "green");
List<Apple> redApples = filterApplesByColor(inventory, "red");

“要是能区分轻的苹果和重的苹果就太好了。重的苹果一般是重量大于150克。”

public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight){List<Apple> result = new ArrayList<>();for(Apple apple: inventory){if(apple.getWeight() > weight){result.add(apple);}}return result;
}

发现有重复代码,打破DRY(Don’t Repeat Yourself)原则

第三次尝试:对你能想到的每个属性做筛选

public static List<Apple> filterApples(List<Apple> inventory, int weight, String color, boolean flag){List<Apple> result = new ArrayList<Apple>();for (Apple apple: inventory){if ( (flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight) ){result.add(apple);}}return result;
}

你可以这么用(但真的很笨拙)

List<Apple> greenApples = filterApples(inventory, "green", 0, true);
List<Apple> heavyApples = filterApples(inventory, "", 150, false);

行为参数化

定义一个接口来对选择标准建模:

interface ApplePredicate{public boolean test(Apple a);
}

现在你就可以用ApplePredicate的多个实现代表不同的选择标准了

public class AppleWeightPredicate implements ApplePredicate{public boolean test(Apple apple){return apple.getWeight() > 150; }
}public class AppleColorPredicate implements ApplePredicate{public boolean test(Apple apple){return "green".equals(apple.getColor());}
}

这里有 策略模式 的影子

策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

该怎么利用ApplePredicate的不同实现呢?你需要filterApples方法接受ApplePredicate对象,对Apple做条件测试。这就是行为参数化:让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为。

第四次尝试:根据抽象条件筛选

public static List<Apple> filter(List<Apple> inventory, ApplePredicate p){List<Apple> result = new ArrayList<>();for(Apple apple : inventory){if(p.test(apple)){result.add(apple);}}return result;
}

传递代码/行为

public class AppleRedAndHeavyPredicate implements ApplePredicate{public boolean test(Apple apple){return "red".equals(apple.getColor()) && apple.getWeight() > 150; }
}List<Apple> redAndHeavyApples = filter(inventory, new AppleRedAndHeavyPredicate());

filterApples方法的行为取决于你通过ApplePredicate对象传递的代码。换句话说,你把filterApples方法的行为参数化了!

多种行为,一个参数

行为参数化的好处在于你可以把迭代要筛选的集合的逻辑与对集合中每个元素应用的行为区分开来。这样你可以重复使用同一个方法,给它不同的行为来达到不同的目的。


编写灵活的prettyPrintApple方法

编写一个prettyPrintApple方法,它接受一个Apple的List,并可以对它参数化,以多种方式根据苹果生成一个String输出

public static void prettyPrintApple(List<Apple> inventory, ???){for(Apple apple: inventory) {String output = ???.???(apple);System.out.println(output);}
}

首先

public interface AppleFormatter{String accept(Apple a);
}

然后

public class AppleFancyFormatter implements AppleFormatter{public String accept(Apple apple){String characteristic = apple.getWeight() > 150 ? "heavy" : "light";return "A " + characteristic + " " + apple.getColor() +" apple";}
}public class AppleSimpleFormatter implements AppleFormatter{public String accept(Apple apple){return "An apple of " + apple.getWeight() + "g";}
}

最后

public static void prettyPrintApple(List<Apple> inventory, AppleFormatter formatter){for(Apple apple: inventory){String output = formatter.accept(apple);System.out.println(output);}
}

运用

prettyPrintApple(inventory, new AppleFancyFormatter());
prettyPrintApple(inventory, new AppleSimpleFormatter());

输出

A light green apple
A heavy red apple
…

An apple of 80g
An apple of 155g
…

对付啰嗦

人们都不愿意用那些很麻烦的功能或概念。目前,当要把新的行为传递给filterApples方法的时候,你不得不声明好几个实现ApplePredicate接口的类,然后实例化好几个只会提到一次的ApplePredicate对象。这真是很啰嗦,很费时间

匿名类

匿名类和你熟悉的Java局部类(块中定义的类)差不多,但匿名类没有名字。它允许你同时声明并实例化一个类。换句话说,它允许你随用随建。

第五次尝试:使用匿名类

List<Apple> redApples = filterApples(inventory, new ApplePredicate() {public boolean test(Apple apple){return "red".equals(apple.getColor());}
});

但匿名类还是不够好。第一,它往往很笨重,因为它占用了很多空间。第二,很多程序员觉得它用起来很让人费解。

鼓励程序员使用行为参数化模式,通过引入Lambda表达式——一种更简洁的传递代码的方式。

第六次尝试:使用Lambda表达式

List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));

第七次尝试:将List类型抽象化

目前,filterApples方法还只适用于Apple。还可以将List类型抽象化,从而超越你眼前要处理的问题。

public interface Predicate<T>{boolean test(T t);
}public static <T> List<T> filter(List<T> list, Predicate<T> p){List<T> result = new ArrayList<>();for(T e: list){if(p.test(e)){result.add(e);}}return result;
}

List<Apple> redApples = filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);

真实的例子

用Comparator来排序

// java.util.Comparator
public interface Comparator<T> {public int compare(T o1, T o2);
}inventory.sort(new Comparator<Apple>() {@Overridepublic int compare(Apple o1, Apple o2) {return o1.getWeight().compareTo(o2.getWeight());}
});inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

用Runnable执行代码块

//行为参数化
Thread t = new Thread(new Runnable() { @Overridepublic void run() {System.out.println("Hello, World!");}
});t = new Thread(()->System.out.println("Hello, World!")) ;

GUI 事件处理

Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {public void handle(ActionEvent event) {label.setText("Sent!!");}
});//行为参数化
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));

小结

  • 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
  • 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。
  • 传递代码,就是将新行为作为参数传递给方法。但在Java 8之前这实现起来很啰嗦。为接口声明许多只用一次的实体类而造成的啰嗦代码,在Java 8之前可以用匿名类来减少。
  • Java API包含很多可以用不同行为进行参数化的方法,包括排序、线程和GUI处理。

《Java8实战》笔记(02):通过行为参数传递代码相关推荐

  1. Java8实战笔记--组合异步编程

    一.Future 初衷是对将来某个时刻会发生的结果进行建模. 想象成这样的场景:你拿了一袋子衣 服到你中意的干洗店去洗.干洗店的员工会给你张发票,告诉你什么时候你的衣服会洗好(这就 是一个Future ...

  2. LaTex实战笔记 4-插入 Python 程序代码块

    LaTex插入 Python 程序代码块 1. 需求描述和解决方案 2. minted 包的安装与配置 2.1 安装 Python 第三方库 Pygments 2.2 下载和安装 minted 宏包 ...

  3. 《Java8实战》笔记汇总

    <Java8实战>笔记(01):为什么要关心Java8 <Java8实战>笔记(02):通过行为参数传递代码 <Java8实战>笔记(03):Lambda表达式 & ...

  4. 《Java8实战》读书笔记06:Parallel Stream 并行流

    <Java8实战>读书笔记06:Parallel Stream 并行流 第7章 并行数据处理与性能 7.1 并行流 7.1.1 将顺序流转换为并行流 7.1.2 测量流性能 7.1.3 正 ...

  5. 《Java8实战》-第六章读书笔记(用流收集数据-01)

    用流收集数据 我们在前一章中学到,流可以用类似于数据库的操作帮助你处理集合.你可以把Java 8的流看作花哨又懒惰的数据集迭代器.它们支持两种类型的操作:中间操作(如 filter 或 map )和终 ...

  6. lambda 两个list获取交集_《Java8 实战》笔记 - Lambda 表达式

    Lambda 表达式介绍 ​ 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它 有参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表.这个定义够大的,让我 ...

  7. 《Java8实战》读书笔记10:组合式异步编程 CompletableFuture

    <Java8实战>读书笔记10:组合式异步编程 CompletableFuture 第11章 CompletableFuture:组合式异步编程 11.1 Future 接口 (只是个引子 ...

  8. Java8实战学习笔记(三)——函数式数据处理

    一.引入流 (一).引言 1.流是什么 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现).可以看成遍历数据集的高级迭代器. 流可以透明地并行 ...

  9. 尚硅谷Docker实战教程-笔记02【安装docker、镜像加速器配置】

    尚硅谷大数据技术-教程-学习路线-笔记汇总表[课程资料下载] 视频地址:尚硅谷Docker实战教程(docker教程天花板)_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01[理念简介 ...

最新文章

  1. [转]hibernate------HQL总结
  2. 请重新认识你作为程序员的价值
  3. Hoogle之装饰模式设计手机(下)
  4. 人工构造迭代次数高度简并的神经网络训练集
  5. Prototype原型模式(创建型模式)
  6. 【BZOJ 3636】教义问答手册 (分治+整体二分+dp)
  7. Python保存任意长度的matplotlib动画为GIF动图
  8. PHP list() 函数
  9. 【数模】模糊综合评价模型
  10. iOS 视频播放器 VLC的集成和基本使用
  11. windows破解锁屏密码(亲测有效:再也不怕别人锁屏防你啦!)
  12. 刘元普双生贵子(但行好事,莫问前程)
  13. XCOM2.0接收数据为0
  14. 微商怎么推广引流?学会玩豆瓣让精准流量源源不断
  15. 3 非齐次线性微分方程与无量纲化
  16. WC2015简短感想
  17. 看板方法:寻找切入点 | Agilean学院 | David博客系列 | 刘永鹏 译,杨柳 校、李淳 审...
  18. golang关于panic的解析
  19. エロエロ王国 1.52 汉化补丁 发布
  20. 无光盘安装winxp

热门文章

  1. oracle用户获取datameta权限,Oracle数据库提权(低权限提升至dba)
  2. java vo转map_JAVA Map转换为Bean或VO
  3. 山西计算机网络技术专升本分数线_2020山西成考专升本招生补录第一批公告!附补录院校专业缺额表!...
  4. gridview databind 会导致页面刷新马_Innodb批量页面刷盘情况下的quot;两次写quot;
  5. 【转】医学影像处理相关知识整理(一)
  6. 【转】Castle中AdditionalInterfaces用法介绍
  7. 【转】!Dynamics 365 Online通过OAuth 2 Client Credential授权(Server-to-Server Authentication)后调用Web API
  8. WebApi系列(从.Net 到 .Net Core)【更新】
  9. 了解 SharePoint 2010 开发中的关键设计决定
  10. Java 并发基础——线程安全性