Java8特性详解(一):行为参数化--将代码传递给方法

什么是行为参数化

行为参数化是使方法接受多种行为作为参数,并在内部使用,完成不同的行为。行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量,是可以帮助你处理频繁变更的需求的一种软件开发模式。

需要指出的是,行为参数化不是Java8的新特性,但是是Java8新特性的重要基础和思想。

举例解释:假设有这样一个场景,小明开车去超市买东西,我们可以定义一个goAndBuy()方法。但针对另一个场景,小明开车去医院挂号或者去动物园看猩猩,使用goAndBuy方法显然是不正确的。如果我们定义go方法,将买东西、挂号、看猩猩等不同行为作为参数,送给go方法执行,则能够很好地适应新场景。

理解行为参数化的经典案例–苹果的故事

案例中不断变换需求,使得不得不重构代码应对新的变化。

1 定义苹果对象类

public static class Apple {

    private int weight; // 重量
    private String color; // 颜色

    public Apple(int weight, String color) {
        this.weight = weight;
        this.color = color;
    }

    public Integer getWeight() {
        return weight;
    }

    public void setWeight(Integer weight) {
        this.weight = weight;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Apple{" + "weight=" + weight + ", color='" + color + '\'' + '}';
    }
}

2 根据颜色筛选苹果(值参数化)

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;
}

List<Apple> greenApples = filterGreenApples(inventory);

针对于苹果存货列表inventory,我们做遍历操作,轻而易举地获取到绿色苹果列表。此时,修改需求转为选出绿色苹果,我们会修改上述方法:

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> redApples = filterApplesByColor(inventory, "red");

通过添加color形参,解决了不同颜色水果的问题。然而Apple类有多个属性,此时需求改成按照苹果重量筛选,以及按照苹果颜色和重量筛选,上面的方法便不能很好的应对,也容易造成代码的冗余。

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;
}

List<Apple> weightBiggerThan120Apples = filterApplesByWeight(inventory, 120);

3 使用匿名内部类进行优化

显而易见,方法代码中存在大量重复代码,唯一不同的便是筛选条件不同。我们将筛选这一行为,从filterApples方法中单独取出。

定义筛选条件接口

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

同时继续修改过滤函数

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

方法调用–采用的策略是绿色苹果,重量大于150g。

List<Apple> heavyApplesByAnonyInnerClass = filterApples2(inventory, new ApplePredicate() {
    @Override
    public boolean test(Apple apple) {
        return "green".equals(apple.color) && apple.getWeight() > 150;
    }
});

定义ApplePredicate接口,作为对每个Apple元素判断行为的策略规范。给filterApples方法添加一个参数,让它接受ApplePredicate对象,这就相当于将对Apple判断行为test传递给了filterApples方法。此时,我们可以任意修改策略,来满足不同场景的需求,而不需要变更filterApples方法。

这一过程即是行为参数化:让方法接受多种行为(策略)做参数,并表现出不同目的。行为参数化,很好的将迭代整个Apple列表的操作与针对每个Apple元素的操作做了解耦,从而能够重复使用同一个方法,给予不同行为参数,获取不同目的。

行为参数化与策略模式息息相关,定义一系列算法,将它们封装起来,并且使他们可相互替换。本小节使用的是匿名内部类,如果传入的是实现类,可以更加直观的理解策略模式的使用。

4 使用Lambda表达式继续优化

在Java8之前,方法只能接受对象。因此对Apple元素的判断行为代码必须要包裹在对象(ApplePredicate对象)中传递给filterApples方法。同时匿名内部类虽然优于具体实现类,但仍有一些不必要代码,并且易读性差。

Java8开始,我们有了更好的解决方案–使用Lambda表达式,清晰易读,代码量少。

List<Apple> heavyApplesLambda = filterApples2(inventory, 
	apple -> "green".equals(apple.color) && apple.getWeight() > 150);

5 小结

在这里插入图片描述
值参数化中的值,包含基本类型、对象(对象引用)。Java8之前我们只能将基本类型和对象引用传递给方法。上方的类和匿名类代码实现上是值参数化(传递类给方法),将其归于行为参数化,是因为他们本身只是为了传递行为。Java8之后,
可以将方法引用和lambda代码传递给方法,更加体现行为参数化这一软件开发模式–将代码传递给方法。

参考资料

  1. Java8实战
  2. 设计模式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章