《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

欢迎收藏和转发,感谢你的支持!(??????)??

欢迎关注我的公众号:庄里程序猿,读书笔记教程资源第一时间获得!

原文地址:https://www.cnblogs.com/HelloDeveloper/p/11404523.html

时间: 2024-10-08 10:20:51

《Java 8 in Action》Chapter 2:通过行为参数化传递代码的相关文章

Android(java)学习笔记165:Android下编写单元测试代码(Junit Test)

编写android应用的时候,往往我们需要编写一些业务逻辑实现类,但是我们可能不能明确这个业务逻辑是否可以成功实现,特别是逻辑代码体十分巨大的时候,我们不可能一行一行检查自己的代码,为了解决这样的问题就出现了: Android下编写单元测试代码-----Junit Test       测试逻辑是:在Eclipse我们待测试项目中编写测试代码,然后运行测试代码,系统会把代码布署到模拟器或者真机中,代码运行之后,会反馈测试结果给Eclipse,用户就知道业务逻辑类是否可以成功实现. 首先我们明确A

Java三大器之拦截器(Interceptor)的实现原理及代码示例

前言:前面2篇博客,我们分析了Java中过滤器和监听器的实现原理,今天我们来看看拦截器. 1,拦截器的概念    java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式.在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或者之后加入某些操作.目前,我们需要掌握的主要是Spring的拦截器,Struts2的拦截器

java文本、表格word转换生成PDF加密文件代码下载

原文:java文本.表格word转换生成PDF加密文件代码下载 代码下载地址:http://www.zuidaima.com/share/1550463239146496.htm 这个实现了PDF加密功能,和一些基本的问题. java文本.表格word转换生成PDF加密文件代码下载,布布扣,bubuko.com

java 线程返回值,优先级,后台线程 示例代码

ava 线程返回值,休眠,优先级,后台线程  示例代码 package org.rui.thread.basic; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Execu

Java实现文件自动打包成zip并下载的代码

import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipOutputStreamDemo { public static void main(String[] args) throws Exception { by

java 队列、优先级队列、双向队列示例演示代码

package org.rui.collection2.queues; import java.util.LinkedList; import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Li

关于JAVA中事件分发和监听机制实现的代码实例-绝对原创实用

http://blog.csdn.net/5iasp/article/details/37054171 文章标题:关于JAVA中事件分发和监听机制实现的代码实例 文章地址: http://blog.csdn.net/5iasp/article/details/37054171 作者: javaboy2012Email:[email protected]qq:    1046011462 一.场景假设 假设有博客系统中需要实现如下功能: 系统中用户发布文章,修改文章,删除文章时,需要一些相关的操作

java学习中,匿名函数、构造方法、构造代码块、构造方法中调用构造方法(java 学习中的小记录)

java学习中,匿名函数.构造方法.构造代码块.构造方法中调用构造方法(java 学习中的小记录) 作者:王可利(Star·星星) 匿名函数 匿名对象:没有名字的对象 匿名对象使用的注意点: 1.一般不会用匿名对象给属性赋值,无法获取属性值,每次new 都是一个新的对象. 2.匿名对象永远都不可能是一个对象. 如:person new().name = "星星":是不行的 匿名对象的好处:书写简单. 匿名对象使用的场景: 1.如果一个对象调用一个方法一次的时候,就可以用匿名对象来调用.

java中使用FIFO队列:java.util.Queue实现多台服务器发邮件的代码

代码下载地址:http://www.zuidaima.com/share/1838230785625088.htm 原文:java中使用FIFO队列:java.util.Queue实现多台服务器发邮件的代码 最近由于zuidaima.com注册用户的增多,qq企业邮箱发送邮件会被封禁账号导致注册后面的用户收不到周总结,所以紧急开发了一套多账号,多服务器发送邮件的程序. 大概的设计思路如下: 1.服务器可以无限扩展,但由于qq企业邮箱是限定域名,所以要想多服务器还得有多域名,多账号也不行. 2.最