博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java8 in action(1) 通过行为参数化传递代码--lambda代替策略模式
阅读量:4329 次
发布时间:2019-06-06

本文共 6131 字,大约阅读时间需要 20 分钟。

猪脚:以下内容参考《Java 8 in Action》

发布:https://ryan-miao.github.io/2017/07/15/java8-in-action-2/

源码:

需求

果农需要筛选苹果,可能想要绿色的,也可能想要红色的,可能想要大苹果(>150g),也可能需要红的大苹果。基于此等条件,编写筛选的代码。

1. 策略模式解决方案

1.1 最直观的做法

首先,已知信息是一筐苹果(List<Apple> inventory),但筛选条件多种多样。我们可以根据不同的条件写不同的方法来达到目的。比如,找出绿色的苹果:

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

同样的,可以编写filterRed, filterWeight等等。但必然出现重复代码,违反软件工程原则Don't repeast yourself。而且,筛选的类也会显得臃肿。

现在,有一种更容易维护,更容易阅读的策略模式来实现这个需求。

1.2 策略模式

由于多种筛选条件的结果都是返回一个boolean值,那么可以把这个条件抽取出来,然后在筛选的时候传入条件。这个筛选条件叫做谓词

创建谓词接口:

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

添加几个判断条件:

public class AppleGreenColorPredicate implements ApplePredicate {    @Override    public boolean test(Apple apple) {        return "green".equals(apple.getColor());    }}public class AppleHeavyWeightPredicate implements ApplePredicate {    @Override    public boolean test(Apple apple) {        return apple.getWeight() > 150;    }}public class AppleRedAndHeavyPredicate implements ApplePredicate {    @Override    public boolean test(Apple apple) {        return "red".equals(apple.getColor()) && apple.getWeight() >150;    }}

筛选的方法:

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

这样,我们就可以根据不同的条件进行筛选了。

List
inventory = new ArrayList<>();inventory.add(new Apple("red", 100));inventory.add(new Apple("red", 200));inventory.add(new Apple("green", 200));List
redHeavyApples = filterApples(inventory, new AppleRedAndHeavyPredicate());Assert.assertEquals(1, redHeavyApples.size());Assert.assertEquals(200, redHeavyApples.get(0).getWeight());

以上的代码设计方案几乎是最好理解和扩展的了,当条件发生改变的时候只要增加一个类就可以。但java8提供了更好的选择,一种你只要声明一个接口,具体实现不用管,只有当使用的时候才去关心。

1.3 方法传递

java8提供了把方法当做参数传递的能力。这样,上面的代码就可以这样写:

List
apples = filterApples(inventory, apple -> "red".equals(apple.getColor()) && apple.getWeight() > 150);Assert.assertEquals(1, apples.size());Assert.assertEquals(200, apples.get(0).getWeight());

除了接口声明,不需要实现接口的类。我们只需要传入一个类似匿名内部类的东西,是的,lambda表达式和匿名内部类是可以互相转换的。

如此,我们设计接口的时候只要声明一个接口作为参数,然后再调用的时候把逻辑当做参数传进去。这个在我看来就是传递方法了。就像Javascript,可以把一个方法当做参数。

与之前的设计模式相比,lambda可以不用写那么类。

1.4 新需求

现在,果农需要包装苹果。包装的方式有多种,我将包装的结果打印出来,就是打印的样式也有多种。比如:

A light green apple

或者

An apple of 150g

上面是两种打印方式,按照之前的策略模式需要创建两个类。下面采用lambda来实现。

public interface AppleFormatter {    String format(Apple apple);}public class AppleOutput{    public static void prettyPrintApple(List
inventory, AppleFormatter formatter){ for (Apple apple : inventory) { String format = formatter.format(apple); System.out.println(format); } } public static void main(String[] args){ List
inventory = new ArrayList<>(); inventory.add(new Apple("red", 100)); inventory.add(new Apple("red", 200)); inventory.add(new Apple("green", 200)); prettyPrintApple(inventory, new AppleFormatter() { @Override public String format(Apple apple) { String characteristic = apple.getWeight()>150?"heavy":"light"; return "A " + characteristic + " " + apple.getColor() + " apple."; } }); prettyPrintApple(inventory, apple -> "An apple of " + apple.getWeight() + "g"); }}

控制台打印:

A light red apple.A heavy red apple.A heavy green apple.An apple of 100gAn apple of 200gAn apple of 200g

如果使用IntelIJ IDEA作为编辑器,那么肯定会忍受不了匿名内部类,因为IDEA会不停的提示你:匿名内部类可以转变为方法参数。

1.5 更普遍的用法

上面的筛选只是针对Apple的,那么是否可以推广开来呢?下面针对List类型抽象化来构造筛选条件。

创建一个条件接口:

public interface Predicate
{ boolean test(T t);}

更新一个更普遍的filter:

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

那么,可能这样用:

public static void main(String[] args) {    List
appleList = new ArrayList<>(); appleList.add(new Apple("red", 100)); appleList.add(new Apple("red", 160)); appleList.add(new Apple("green", 60)); List
redApples = filter(appleList, (Apple apple) -> "red".equals(apple.getColor())); Assert.assertEquals(2, redApples.size()); List
numberList = Arrays.asList(1,2,3,4,5,6,7,8,9); List
lessThan4Numbers = filter(numberList, (Integer num) -> num < 4); Assert.assertEquals(3, lessThan4Numbers.size());}

1.6 排序

行为参数化的过程掌握后,很多东西就会自然而然的使用了。比如排序。果农需要将苹果按照大小排序呢?

java8中List是有默认方法的:

default void sort(Comparator
c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator
i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); }}

其实就是将以前手动排序封装了。那么,苹果的排序就可以传入一个比较器实现:

@Testpublic void sort(){    List
appleList = new ArrayList<>(); appleList.add(new Apple("red", 100)); appleList.add(new Apple("red", 160)); appleList.add(new Apple("green", 60)); appleList.sort((o1, o2) -> o1.getWeight()-o2.getWeight());}

根据IDEA的提示,进一步:

appleList.sort(Comparator.comparingInt(Apple::getWeight));

这里就涉及了多次行为传参了。后面再说。

1.7 Runnable

多线程Runnable的时候经常会采用匿名内部类的做法:

@Testpublic void testRunnable(){    Runnable runnable = new Runnable() {        @Override        public void run() {            System.out.println("running");        }    };    new Thread(runnable).start();}

采用lambda行为传参就变为:

@Testpublic void testRunnable(){    Runnable runnable = () -> System.out.println("running");    new Thread(runnable).start();}

小结

本次测试主要理解如下内容:

  • 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
  • 传递代码,就是将行为作为参数传递给方法。

参考

转载于:https://www.cnblogs.com/woshimrf/p/java8-in-action-2.html

你可能感兴趣的文章
【解决Chrome浏览器和IE浏览器上传附件兼容的问题 -- Chrome关闭flash后,uploadify插件不可用的解决办法】...
查看>>
34 帧动画
查看>>
二次剩余及欧拉准则
查看>>
Centos 7 Mysql 最大连接数超了问题解决
查看>>
thymeleaf 自定义标签
查看>>
关于WordCount的作业
查看>>
C6748和音频ADC连接时候的TDM以及I2S格式问题
查看>>
UIView的layoutSubviews,initWithFrame,initWithCoder方法
查看>>
STM32+IAP方案 实现网络升级应用固件
查看>>
用74HC165读8个按键状态
查看>>
jpg转bmp(使用libjpeg)
查看>>
linear-gradient常用实现效果
查看>>
sql语言的一大类 DML 数据的操纵语言
查看>>
VMware黑屏解决方法
查看>>
JS中各种跳转解析
查看>>
JAVA 基础 / 第八课:面向对象 / JAVA类的方法与实例方法
查看>>
Ecust OJ
查看>>
P3384 【模板】树链剖分
查看>>
Thrift源码分析(二)-- 协议和编解码
查看>>
考勤系统之计算工作小时数
查看>>