Java8学习笔记之行为参数化

行为参数化:帮助你处理频繁变更的需求的一种软件开发模式,即可以将代码块作为参数传递给另一个方法,稍后再去执行它。此时这个方法的行为就基于那块代码被参数化了。

行为参数化就是让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为。 

特点:

1)可以对列表中的每个元素做“某件事”

2)可以在列表处理完后做“另一件事”

3)遇到错误时可以做“另外一件事”

示例1:实现从一个列表中筛选出绿色苹果?

实现方式有如下几种:

方式一:遍历筛选

public static List<Apple> filterGreenApples(List<Apple> list) { 

    List<Apple> result = new ArrayList<>();

    for(Apple apple: list){

        if("green".equals(apple.getColor()) result.add(apple);

    }

    return result;

}

此种方式可以实现筛选绿苹果的要求,但是如果想要筛选更多颜色的苹果就不能满足需求了,必须复制更多的filterGreenApples方法或者在循环体中增加各种if条件判断来解决,带来的问题是代码重复且复用率低,维护困难。

方式二:把颜色作为参数,适应不同颜色

public static List<Apple> filterApplesByColor(List<Apple> list, String color) {

    List<Apple> result = new ArrayList<>();

    for (Apple apple: list){

        if (apple.getColor().equals(color)) result.add(apple); 

    }

    return result;

}

此种方式可以解决颜色筛选问题,但是如果想要按重量筛选该如何处理呢?若将上述方法复制一份改为按重量过滤,则会带来代码的重复,若想更改遍历方式提升性能,就必须更改所有的方法,代价太大。

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

public static List<Apple> filterApples(List<Apple> list, String color, int weight, boolean flag) {

    List<Apple> result = new ArrayList<>();

    for (Apple apple: list){

        if ((flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight))

            result.add(apple);

    }

    return result;

}

此方式虽然可以解决上述需求,但是实现效果很差,并不推荐使用。如果进行组合属性或更复杂的查询,将会有多个重复的filter方法或是非常复杂的参数,维护起来会非常困难。

方式四、对选择标准建模,根据抽象条件筛选(策略模式)

你需要一种比添加很多参数更好的方法来应对变化的需求,一种可能的解决方案是对你的选择标准建模:如根据Apple的某些属性来返回一个boolean值。我们把它称为谓词(即一个返回boolean值的函数)。

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

public interface ApplePredicate{

    boolean test (Apple apple);

}

此时你就可以用ApplePredicate的多个实现代表不同的选择标准了,比如:

public class AppleHeavyWeightPredicate implements ApplePredicate{

    public boolean test(Apple apple){return apple.getWeight() > 150;} //筛选重量

}

public class AppleGreenColorPredicate implements ApplePredicate{

    public boolean test(Apple apple){return "green".equals(apple.getColor());} //筛选颜色

}

使用ApplePredicate改进筛选方法:

public static List<Apple> filterApples(List<Apple> list, ApplePredicate p){

    List<Apple> result = new ArrayList<>();

    for(Apple apple: inventory){

        if(p.test(apple)) result.add(apple); //谓词对象封装了测试苹果的条件

    }

    return result;

}

此时代码看起来更加简洁灵活和清晰易用了,可以方便的根据不同的条件应对不同的需求变更。例如:找出所有重量超过150克的红苹果。

public class AppleRedAndHeavyPredicate implements ApplePredicate{

    public boolean test(Apple apple){

        return "red".equals(apple.getColor()) && apple.getWeight() > 150;

    }

}

List<Apple> redAndHeavyApples = filterApples(inventory, new AppleRedAndHeavyPredicate());

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

17879487-94dd18f3be3fb29f.png
参数化filterApples的行为,并传递不同的筛选策略

方式五、使用匿名类

上述方式在需要把新的行为传递给filterApples方法时,你不得不声明好几个实现ApplePredicate接口的类,然后实例化多个只会用到一次的ApplePredicate对象,让代码看起来比较啰嗦。如何改进?Java有一个机制称为匿名类,它可以让你同时声明和实例化一个类,帮你改善代码变得更简洁。

通过创建一个用匿名类实现ApplePredicate的对象,重写筛选:

List<Apple> redApples = filterApples(inventory, new ApplePredicate() { //直接内联参数化 filterapples方 法的行为

    public boolean test(Apple apple){ return "red".equals(apple.getColor());}

}); 

匿名类的缺点:比较笨重,占用空间较大,其次代码不容易理解;

17879487-9d599beca9d276d5.png

答案:5,this指的是包含它的Runnable,而不是外面的类MeaningOfThis。

方式六、使用Lambda表达式(推荐)

用Lambda表达式重写上述代码:

List 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){ //引入泛型类型参数T

    List<T> result = new ArrayList<>();

    for(T e: list){

        if(p.test(e)) result.add(e);

    }

    return result;

}

示例2:用Comparator来排序

在Java 8中,List自带了一个sort方法(或用Collections.sort)。sort的行为可以用java.util.Comparator对象来参数化:

public interface Comparator<T> {public int compare(T o1, T o2);}

你可以创建Comparator的实现,用sort方法表现出不同的行为。比如,你可以使用匿名类,按照重量升序对库存排序:

1>普通方式

inventory.sort(new Comparator<Apple>() {

    public int compare(Apple a1, Apple a2){

        return a1.getWeight().compareTo(a2.getWeight());

    }

});

2>Lambda方式

inventory.sort( (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

示例3:用Runnable执行代码块

Runnable接口表示一个要执行的代码块,代码不会返回任何结果 ;

public interface Runnable{public void run();}

1>普通方式

Thread t = new Thread(new Runnable() {

    public void run(){ 

        System.out.println("Hello world");

    }

});

2>Lambda方式

Thread t = new Thread(() -> System.out.println("Hello world"));

                                                                                       以上示例摘自《Java8实战》

转载于:https://www.jianshu.com/p/a8c3effa61dc

猜你喜欢

转载自blog.csdn.net/weixin_34236497/article/details/91095583
今日推荐