Java8 -- 01 -- 行为参数化

原文链接:Java8 – 01 – 行为参数化


相关文章:


Java8中出现了许多新特性,行为参数化就是其中之一,所谓行为参数化,就是可以帮助你处理频繁变更的需求的一种软件开发模式;简单来说,就是拿出一个代码块,把它准备好却不执行它,这个代码块以后可以被程序的其他部分调用,这就意味着我们可以推迟这个代码块的执行

在软件开发中,一个众所周知的问题就是,不管你做什么,需求总是会在变。比如说,有个程序帮助农民了解自己的库存,第一天农民想知道库存中绿色的苹果有多少个,你写好了代码,但第二天,农民想知道自己的库存中有多少重量大于150克的,这次你又修改好了代码,之后农民又想知道绿色的和重量大于150克的苹果有多少,这就需要我们修改多次程序。理想状态下,我们应该把工作量降到最少,此外,类似的新功能实现起来还应该简单,而且易于长期维护


以下是一个经典的例子:筛选苹果,通过这个例子,我们可以很清晰地看到行为参数化带来的简便之处

  • Apple.java

    public class Apple {
    
        private String color;
        private Integer weight;
    
        public Apple(String color, Integer weight) {
            this.color = color;
            this.weight = weight;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public Integer getWeight() {
            return weight;
        }
    
        public void setWeight(Integer weight) {
            this.weight = weight;
        }
    
        @Override
        public String toString() {
            return "Apple{" +
                    "color='" + color + '\'' +
                    ", weight=" + weight +
                    '}';
        }
    }
    

例子一

  • 假设我们有一个 Apple 类,它有 color 和 weight 两个属性,此时我们还有一个变量 inventory 保存着一个 Apple 的列表,此时我们需要帮助农民筛选出所有的绿苹果,这很简单我们可以这么做

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

例子二

  • 此时农民又改变主意了,他想要筛选出所有的红苹果,这个也简单,只需要将上面的代码中 green 改成 red 就可以了,可万一农民又想要其他颜色苹果,那样的话又要改动代码,因此我们可以将颜色给抽象化

    public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (color.equals(apple.getColor())) {
                result.add(apple);
            }
        }
        return result;
    }
    

例子三

  • 这时候农民又来了,说是要帮他筛选出重量大于 150 克的苹果,好吧,之前写的方法都用不到了,重新写一个吧,同时我们将重量给抽象化

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

例子四

  • 过了一天,农民又说,再帮我筛选下重量大于 150 克的红苹果,我的天呐又改需求,有完没完啊,也就在内心吐槽一下,转头又去撸代码了

    public static List<Apple> filterApples(List<Apple> inventory, String color, int weight) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (apple.getColor().equals(color) && apple.getWeight() > weight) {
                result.add(apple);
            }
        }
        return result;
    }
    

例子五

  • 这几天,农民都没有来,过了几天潇洒的生活,可转眼一想不对啊,万一哪天农民又来了,那我们不是还是要重构代码,有没有什么其他好的办法呢?

  • 这个时候,行为参数化就出场了,我们考虑的是苹果,需要根据苹果的某些属性来返回一个 boolean 值,我们称之为谓词 (即一个返回 boolean 值的函数),因此我们先定义一个接口来对选择标准建模

    public interface ApplePredicate {
    
        boolean test(Apple apple);
    }
    ``
    
    
  • 此时,我们就可以通过使用 ApplePredicate 的多个实现来表示不同的选择标准了,我们可以将这些标准看作是 filter 方法的不同行为,这与策略模式 (定义一族算法,把它们封装起来,可以称之为策略,然后在运行时选择一个算法) 十分相近,在这里,算法族就是 ApplePredicate,不同的策略就是不同的具体实现

    public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate predicate) {
        List<Apple> result = new ArrayList<>();
        inventory.forEach(apple -> {
            if (predicate.test(apple)) {
                result.add(apple);
            }
        });
        return result;
    }
    
    public class AppleGreenColorPredicate implements ApplePredicate {
    
        @Override
        public boolean test(Apple apple) {
            return "green".equals(apple.getColor());
        }
    }
    
  • 如上所示,这段代码已经比以前灵活多了,我们可以创建不同的 ApplePredicate 对象,并将它们传递给 filterApples 方法,filterApples 方法的行为取决于我们通过 ApplePredicate 对象传递的代码,换句话说,我们已经把 filterApples 方法的行为参数化了


例子六

  • 但此时我们又会发现,如果使用例子五中的代码,会声明许多只要实例化一次的类,这样会导致十分繁琐,因此我们可以尝试使用匿名内部类来进行改进

    List<Apple> result = filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test(Apple apple) {
            return "green".equals(apple.getColor());
        }
    });
    

例子七

  • 但使用匿名内部类还是不够友好,第一、它往往很笨重,因为它占用了很多空间;第二、会让人看起来很费解,因此我们需要进一步的改进,使用 Lambda 表达式

    List<Apple> result = filterApples(inventory, (Apple apple) -> "green".equals(apple.getColor()));
    
  • 还可以将类型去掉,进一步简便

    List<Apple> result = filterApples(inventory, apple -> "green".equals(apple.getColor()));
    
  • 到了这一步,我们会发现只需要一行代码就可以用来代替之前的多行代码,是不是干净很多了呢!


例子八

  • 当前 filterApples 方法还只适用于 Apple,可万一哪一天,农民朋友说再帮我筛选下香蕉、西瓜等等其他水果,那又该怎么办呢?所以我们可以更进一步,将 List 类型抽象化,从而超越眼前要处理的问题

    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        list.forEach(t -> {
            if (predicate.test(t)) {
                result.add(t);
            } 
        });
        return result;
    }
    
  • 现在可以说是大功告成了,filter 方法已经基本满足了农民朋友的所有需求了,是不是很开心~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值