Java8实战——通过行为参数化传递代码

Wesley13
• 阅读 485

1、初试牛刀:筛选绿苹果

第一个解决方案可能是下面这样的:

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

上面代码只针对绿苹果进行筛选,现在,我还想筛选出红苹果,该怎么做呢?简单的解决办法就是重复写一个方法,再改条件为红苹果,但是,要筛选的颜色有多种的情况,这样写会导致代码十分冗余,所以我们第一步尝试将其抽象化。

2、再展身手:把颜色作为参数

public static List<Apple> filterGreenApples(List<Apple> inventory,String color){
        List<Apple> result=new ArrayList<>();
        //颜色作为参数
        for (Apple apple : inventory) {
            if (color.equals(apple.getColor())){
                result.add(apple);
            }
        }
        return result;
    }

但是现在又想根据苹果的重量对苹果进行筛选,那是不是也要用另外一个参数表示苹果重量呢?之后我又想加个标志区分对颜色和重量的查询呢?下面是展示一般写法,但是很傻。

3、第三次尝试:对你能想到的每个属性做筛选

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

4、柳暗花明:行为参数化

我们可以把行为进行参数化,来达到更高层次的抽象,首先定义一个统一的标准接口,再通过不同子类对其进行实现,这有点类似于策略设计模式的赶脚。

//封装了对选择苹果的策略
public interface ApplePredicate {

    //具体算法交给子类去实现
    boolean test (Apple apple);
}

//颜色算法
public class AppleGreenColorPredicate implements  ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "green".equals(apple.getColor());
    }
}
//重量算法
public class AppleHeavyWeightPredicate implements  ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return apple.getWeight()>150;
    }
}

5、第四次尝试:根据抽象条件筛选

   public static List<Apple> filterApples(List<Apple> inventory,ApplePredicate p){
        List<Apple> result = new ArrayList<>();
        //行为参数化
        for (Apple apple : inventory) {
            if (p.test(apple)){
                result.add(apple);
            }
        }
        return result;
    }

我们在使用的时候可以传递不同的策略实现来达到目的

List<Apple> heavyApples = filterApples(inventory, new AppleHeavyWeightPredicate());         
List<Apple> greenApples = filterApples(inventory, new AppleGreenColorPredicate());

但是这样有个问题,就是每个策略我都要定义一个实现类去实现某个算法,导致后面如果有很多策略,会增加很多的类,我们知道使用匿名类也是一种不错的选择

6、第五次尝试:使用匿名类

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

但是问题又来了,匿名类还是不够好,第一,它往往很笨重,占用了很多的空间,第二,使用起来让人费解,导致代码可读性不高,即使匿名类处理在某种程度上改善了为一个接口声明好几个实体类的啰嗦问题,但是还是不能令人满意,自java8引入的lambda表达式——一种更简洁的传递代码的方式解决了这个问题。下面我们利用lambda表达式来改写前面的代码吧

7、第六次尝试:使用Lambda表达式

List<Apple>result= filterApples(inventory, (Apple apple)-> "red".equals(apple.getColor()));

不得不承认,使用lambda表达式改写之前的代码确实干净很多,因为它看起来更像问题陈诉本身了,解决了啰嗦的问题

8、第七次尝试:将List类型抽象化

在通往抽象的路上,我们还可以进一步。目前filterApples方法还只适用Apple,我们还可以尝试适用在其他水果上。

public interface Predicate<T> {
    boolean test(T t);
}

//映入类型参数T
 public static<T> List<T> filter(List<T> list,Predicate<T> p){
        List<T> result =new ArrayList<>();
        for (T e : list) {
            if (p.test(e)){
                result.add(e);
            }
        }
        return result;
    }

现在你可以吧filter方法作用在橘子,香蕉等列表上了。

9、小结

行为参数化,就是一个方法接收不同的行为作为参数,并在内部使用他们,完成不同行为的能力。

行为参数化可以让代码更好的适应不断变化的要求,减轻未来的工作量。

传递代码,就是将新行为作为参数传递给方法,但是在java8之前实现起来很啰嗦。为接口声明许多只用一次的实体类而造成的啰嗦代码,在java8之前可以用匿名类来减少。

java API 包含很多可以用不同行为进行参数化的方法,包括排序、线程等。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
List的Select 和Select().tolist()
List<PersondelpnewList<Person{newPerson{Id1,Name"小明1",Age11,Sign0},newPerson{Id2,Name"小明2",Age12,
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
2年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这