公子奇带你一步一步了解Java8中行为参数化

Posted 公子奇的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了公子奇带你一步一步了解Java8中行为参数化相关的知识,希望对你有一定的参考价值。

说明:因为本公子一直从事监狱软件开发,所以本系列博客的引入也以此为背景。问题做了简化,只是为了来讲解技术点。

一、问题提出

    今日在好好的撸着代码,超哥(民警)找来了,让把监狱30岁以上的民警找给他。

二、功能实现

    这个简单。什么也不用说,代码撸起来。首先定义实体类

package com.hz.pojo;

/**
 * 民警实体类
 */
public class Police {
    /**
     * 民警警号
     */
    private String policeNo;
    /**
     * 民警姓名
     */
    private String policeName;
    /**
     * 民警年龄
     */
    private Integer policeAge;
    /**
     * 民警籍贯
     */
    private String policeNativePlace;

    public Police(String policeNo, String policeName, Integer policeAge, String policeNativePlace) {
        this.policeNo = policeNo;
        this.policeName = policeName;
        this.policeAge = policeAge;
        this.policeNativePlace = policeNativePlace;
    }

    public String getPoliceNo() {
        return policeNo;
    }

    public void setPoliceNo(String policeNo) {
        this.policeNo = policeNo;
    }

    public String getPoliceName() {
        return policeName;
    }

    public void setPoliceName(String policeName) {
        this.policeName = policeName;
    }

    public Integer getPoliceAge() {
        return policeAge;
    }

    public void setPoliceAge(Integer policeAge) {
        this.policeAge = policeAge;
    }

    public String getPoliceNativePlace() {
        return policeNativePlace;
    }

    public void setPoliceNativePlace(String policeNativePlace) {
        this.policeNativePlace = policeNativePlace;
    }

    @Override
    public String toString() {
        return "Police{" +
                "policeNo=‘" + policeNo + ‘‘‘ +
                ", policeName=‘" + policeName + ‘‘‘ +
                ", policeAge=" + policeAge +
                ", policeNativePlace=‘" + policeNativePlace + ‘‘‘ +
                ‘}‘;
    }
}

然后实现

 1 package com.hz;
 2 
 3 import com.hz.pojo.Police;
 4 
 5 import java.util.ArrayList;
 6 import java.util.Arrays;
 7 import java.util.List;
 8 
 9 public class PoliceMain {
10     public static void main(String[] args) {
11         List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
12                 new Police("P001", "李警官", 32, "安徽"),
13                 new Police("P001", "程警官", 25, "安徽"),
14                 new Police("P001", "杨警官", 35, "浙江"));
15 
16         List<Police> result = new ArrayList<>();
17         for (Police police : polices) {
18             if (police.getPoliceAge() > 30) {
19                 result.add(police);
20             }
21         }
22 
23         System.out.println("查询结果:" + result);
24     }
25 }

因为30是个随时会变化的值,我在这里还很明智的将其作为一个参数并提取为一个方法

package com.hz;

import com.hz.pojo.Police;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PoliceMain {
    public static void main(String[] args) {
        List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
                new Police("P001", "李警官", 32, "安徽"),
                new Police("P001", "程警官", 25, "安徽"),
                new Police("P001", "杨警官", 35, "浙江"));

        List<Police> result = filterPoliceAge(polices, 30);

        System.out.println("查询结果:" + result);
    }

    /**
     * 民警过滤器
     * @auth 公子奇
     * @date 2019-01-02
     * @param policeContainer 全部民警
     * @param age 年龄
     * @return 符合结果的民警列表
     */
    static List<Police> filterPoliceAge(List<Police> policeContainer, Integer age) {
        List<Police> result = new ArrayList<>();
        for (Police police : policeContainer) {
            if (police.getPoliceAge() > 30) {
                result.add(police);
            }
        }
        return result;
    }
}

写完后我还沾沾自喜,认为很好的代码,随你年龄怎么变,我都可以。看来我太天真了,很快问题就来了。

三、问题的进一步引入

    没过多久,超哥又来了,问能不能把所有籍贯为浙江的民警给找出来。我一看,很容易啊,等我几分钟,马上就好,代码继续撸起来。

 1 package com.hz;
 2 
 3 import com.hz.pojo.Police;
 4 
 5 import java.util.ArrayList;
 6 import java.util.Arrays;
 7 import java.util.List;
 8 
 9 public class PoliceMain {
10     public static void main(String[] args) {
11         List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
12                 new Police("P001", "李警官", 32, "安徽"),
13                 new Police("P001", "程警官", 25, "安徽"),
14                 new Police("P001", "杨警官", 35, "浙江"));
15 
16         List<Police> result = filterPoliceAge(polices, 30);
17         System.out.println("查询结果1: " + result);
18 
19         System.out.println("-------------");
20 
21         result = filterPoliceNativePlace(polices, "浙江");
22         System.out.println("查询结果2: " + result);
23     }
24 
25     /**
26      * 民警年龄过滤器
27      * @auth 公子奇
28      * @date 2019-01-02
29      * @param policeContainer 全部民警
30      * @param age 年龄
31      * @return 符合结果的民警列表
32      */
33     static List<Police> filterPoliceAge(List<Police> policeContainer, Integer age) {
34         List<Police> result = new ArrayList<>();
35         for (Police police : policeContainer) {
36             if (police.getPoliceAge() > 30) {
37                 result.add(police);
38             }
39         }
40         return result;
41     }
42 
43     /**
44      * 民警籍贯过滤器
45      * @auth 公子奇
46      * @date 2019-01-02
47      * @param policeContainer 全部民警
48      * @param nativePlace 籍贯
49      * @return 符合结果的民警列表
50      */
51     static List<Police> filterPoliceNativePlace(List<Police> policeContainer, String nativePlace) {
52         List<Police> result = new ArrayList<>();
53         for (Police police : policeContainer) {
54             if (nativePlace.equals(police.getPoliceNativePlace())) {
55                 result.add(police);
56             }
57         }
58         return result;
59     }
60 }

写完之后,我发现有点不太对劲啊,filterPoliceAge和filterPoliceNativePlace这两个方法存在大量重复的代码,这个很明显违背了DRY(Don‘t Repeat Yourself,不要重复自己)的软件工程原则。随着后面民警属性的越来越多,这不是要命嘛!

四、策略设计模式的引入

    分析上述任务,代码重复/业务逻辑也差不多,既然如此,策略模式就是很好的解决方案啊!说改就改,代码继续。关于策略模式查看我的GitHub策略模式介绍。

首先定义一个过滤处理接口

 1 package com.hz.filter;
 2 
 3 import com.hz.pojo.Police;
 4 
 5 /**
 6  * 民警过滤条件
 7  */
 8 public interface PolicePredicate {
 9     boolean test(Police police);
10 }

然后针对这个接口,分别实现年龄和籍贯实现类

package com.hz.filter;

import com.hz.pojo.Police;

/**
 * 民警年龄筛选
 */
public class PoliceAgePredicate implements PolicePredicate {
    @Override
    public boolean test(Police police) {
        return police.getPoliceAge() > 30;
    }
}


package com.hz.filter;

import com.hz.pojo.Police;

/**
 * 民警籍贯筛选
 */
public class PoliceNativePlacePredicate implements PolicePredicate {
    @Override
    public boolean test(Police police) {
        return "浙江".equals(police.getPoliceNativePlace());
    }
}

调用

 1 package com.hz;
 2 
 3 import com.hz.filter.PoliceAgePredicate;
 4 import com.hz.filter.PoliceNativePlacePredicate;
 5 import com.hz.filter.PolicePredicate;
 6 import com.hz.pojo.Police;
 7 
 8 import java.util.ArrayList;
 9 import java.util.Arrays;
10 import java.util.List;
11 
12 public class PoliceMain2 {
13     public static void main(String[] args) {
14         List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
15                 new Police("P001", "李警官", 32, "安徽"),
16                 new Police("P001", "程警官", 25, "安徽"),
17                 new Police("P001", "杨警官", 35, "浙江"));
18 
19         List<Police> result = filterPolice(polices, new PoliceAgePredicate());
20         System.out.println("结果1: " + result);
21 
22         System.out.println("--------------");
23 
24         result = filterPolice(polices, new PoliceNativePlacePredicate());
25         System.out.println("结果2: " + result);
26     }
27 
28     /**
29      * 民警筛选器
30      * @param policeList
31      * @param predicate
32      * @return
33      */
34     static List<Police> filterPolice(List<Police> policeList, PolicePredicate predicate) {
35         List<Police> result = new ArrayList<>();
36 
37         for (Police police : policeList) {
38             if (predicate.test(police)) {
39                 result.add(police);
40             }
41         }
42 
43         return result;
44     }
45 }

    到了这里我感觉已经很完美了,引入了设计模式,灵活性大大的增加了,以后不管他怎么变化,我这边只要添加一个实现类就可以了。到此大功告成,走,喝杯咖啡去。

五、设计模式后的进一步思考,匿名类的对比

    咖啡喝完以后,把刚才的代码拿出来又欣赏了一篇,感觉真好。不对!后面筛选条件越来越多,我的实现类也会变的非常多,而且这些实现类都执行一步操作,这个实现类有必要去创建吗?如果不去创建这些实现类,我该怎么实现功能呢?我突然想到了匿名类。那就实现看看呗!

 1 package com.hz;
 2 
 3 import com.hz.filter.PoliceAgePredicate;
 4 import com.hz.filter.PoliceNativePlacePredicate;
 5 import com.hz.filter.PolicePredicate;
 6 import com.hz.pojo.Police;
 7 
 8 import java.util.ArrayList;
 9 import java.util.Arrays;
10 import java.util.List;
11 
12 public class PoliceMain2 {
13     public static void main(String[] args) {
14         List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
15                 new Police("P001", "李警官", 32, "安徽"),
16                 new Police("P001", "程警官", 25, "安徽"),
17                 new Police("P001", "杨警官", 35, "浙江"));
18 
19         List<Police> result = filterPolice(polices, new PoliceAgePredicate());
20         System.out.println("结果1: " + result);
21 
22         System.out.println("--------------");
23 
24         result = filterPolice(polices, new PoliceNativePlacePredicate());
25         System.out.println("结果2: " + result);
26 
27         System.out.println("----------------");
28 
29         result = filterPolice(polices, new PolicePredicate() {
30             @Override
31             public boolean test(Police police) {
32                 return police.getPoliceAge() > 30;
33             }
34         });
35         System.out.println("结果3: " + result);
36     }
37 
38     /**
39      * 民警筛选器
40      * @param policeList
41      * @param predicate
42      * @return
43      */
44     static List<Police> filterPolice(List<Police> policeList, PolicePredicate predicate) {
45         List<Police> result = new ArrayList<>();
46 
47         for (Police police : policeList) {
48             if (predicate.test(police)) {
49                 result.add(police);
50             }
51         }
52 
53         return result;
54     }
55 }

    这样即实现了功能,也不需要创建大量的实现类,类增加的同时,也会增加我们的维护成本。后来仔细想了想,也不太好,类的维护是降低了,可是大量的匿名实现从代码可读性上也是存在很大缺陷的,还有更好的方案吗?

六、Lambda表达式的引入

    以上匿名类,完全是可以通过Java8来进行简化的。话不多说,代码奉上。

package com.hz;

import com.hz.filter.PolicePredicate;
import com.hz.pojo.Police;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PoliceMain3 {
    public static void main(String[] args) {
        List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
                new Police("P002", "李警官", 32, "安徽"),
                new Police("P003", "程警官", 25, "安徽"),
                new Police("P004", "杨警官", 35, "浙江"));

        List<Police> result = filterPolice(polices, (Police police) -> police.getPoliceAge() > 30);
        System.out.println("结果1: " + result);

        System.out.println("---------------");

        result = filterPolice(polices, (Police police) -> "浙江".equals(police.getPoliceNativePlace()));
        System.out.println("结果2: " + result);
    }

    /**
     * 民警筛选器
     * @param policeList
     * @param predicate
     * @return
     */
    static List<Police> filterPolice(List<Police> policeList, PolicePredicate predicate) {
        List<Police> result = new ArrayList<>();

        for (Police police : policeList) {
            if (predicate.test(police)) {
                result.add(police);
            }
        }

        return result;
    }
}

这么一改,代码简洁了很多,也更加容易理解是什么意思了。

七、继续对类型进行抽象化

    刚对民警筛选修改完,感觉不需要再改了,此时超哥带着“美丽的”笑容向我走来了。需要对监狱罪犯也做同样的筛选。我的天啊,难道要我把上面的代码再针对罪犯复制一遍吗?作为一名爱学习、求进步的新时代程序员,这怎么能难到我。

    既然如此,看来要从接口上进行修改了,将接口修改为:

package com.hz.filter;

/**
 * 筛选器
 * @param <T>
 */
public interface Predicate<T> {
    boolean test(T t);
}

这样一改,你需要筛选类型,自己去传就可以了。

 1 package com.hz;
 2 
 3 import com.hz.filter.Predicate;
 4 import com.hz.pojo.Police;
 5 
 6 import java.util.ArrayList;
 7 import java.util.Arrays;
 8 import java.util.List;
 9 
10 public class PoliceMain4 {
11     public static void main(String[] args) {
12         List<Police> polices = Arrays.asList(new Police("P001", "余警官", 27, "浙江"),
13                 new Police("P002", "李警官", 32, "安徽"),
14                 new Police("P003", "程警官", 25, "安徽"),
15                 new Police("P004", "杨警官", 35, "浙江"));
16 
17         List<Police> result = filter(polices, (Police police) -> police.getPoliceAge() > 30);
18         System.out.println("结果1: " + result);
19 
20         System.out.println("---------------");
21 
22         result = filter(polices, (Police police) -> "浙江".equals(police.getPoliceNativePlace()));
23         System.out.println("结果2: " + result);
24     }
25 
26     /**
27      * 筛选器
28      * @param container
29      * @param predicate
30      * @return
31      */
32     static <T> List<T> filter(List<T> container, Predicate<T> predicate) {
33         List<T> result = new ArrayList<>();
34 
35         for (T e : container) {
36             if (predicate.test(e)) {
37                 result.add(e);
38             }
39         }
40 
41         return result;
42     }
43 }

    到此,即实现了行为参数化。关于Java8的一些概念和知识点我们再后续在去介绍。我们将开个系列去详细介绍Java8的使用。

以上是关于公子奇带你一步一步了解Java8中行为参数化的主要内容,如果未能解决你的问题,请参考以下文章

公子奇带你进入Java8流的世界

android-----带你一步一步优化ListView

一个老王开枪案例带你一步一步领略程序开发的魅力

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

带你一步一步搭建TypeScript环境

android-----带你一步一步优化ListView