浅谈Java行为参数化和Lambda表达式
案例分析
在软件工程中,一个众所周知的问题就是,不管你做什么,用户的需求肯定会变。比方说,有个应用程序是帮助农民了解自己的库存的。这位农民可能想有一个查找库存中所有绿色苹果的功能。但到了第二天,他可能会告诉你:“其实我还想找出所有重量超过150克的苹果。”又过了两天,农民又跑回来补充道:“要是我可以找出所有既是绿色,重量也超过150克的苹果,那就太棒了。”你要如何应对这样不断变化的需求?理想的状态下,应该把你的工作量降到最少。此外,类似的新功能实现起来还应该很简单,而且易于长期维护。
下面我们就一步一步的来实现这个农民伯伯的需求:
1、筛选出所有绿色的苹果:
我们首先想到应该是来遍历一个list,从中一个一个比较这些苹果的颜色,把是绿色的苹果放到新的list中,然后返回给农民,告诉他这些都是绿色的苹果,代码如下:
public static List<Apple> filterGreenApples(List<Apple> inventory) {List<Apple> result = new ArrayList<Apple>();for(Apple apple: inventory){if( "green".equals(apple.getColor() ) {result.add(apple);}}return result;
}
好,我们高兴的完成了农民想要的结果,交付给农民,然而,在你交付的那天,农民说他不想要绿色的苹果了,想要红色的苹果,然后你内心是不是有点小小的崩溃,但是客户是上帝,然后你只能默默的回去改了,改的时候想了一下,农民说不定后面还会改变苹果的颜色,然后你就想到了传个颜色的参数来修改这个方法,那么好,下面开始动工了。
2、筛选出任意颜色的苹果:
public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {List<Apple> result = new ArrayList<Apple>();for (Apple apple: inventory){if ( apple.getColor().equals(color) ) {result.add(apple);}}return result;
}
现在,只要像下面这样调用方法,农民朋友就会满意了:
List<Apple> greenApples = filterApplesByColor(inventory, "green");
List<Apple> redApples = filterApplesByColor(inventory, "red");
…
是不是觉得自己分牛逼了,不管农民想要啥颜色的苹果,都能以最小的修改来实现,可是农民伯伯就是要跟你作对呀,他说:“要是能区分轻的苹果和重的苹果就太好了。重的苹果一般是重量大于150克。”作为软件工程师,你早就想到农民可能会要改变重量,于是你写了下面的方法,用另一个参数来应对不同的重量:
3、筛选中较重的苹果:
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {List<Apple> result = new ArrayList<Apple>();for (Apple apple: inventory){if ( apple.getWeight() > weight ){result.add(apple);}}return result;
}
好了,这下农民伯伯没话说了吧,可是有没发现,你已经进入了复制/粘贴的开发模式了,1、2、3这几段代码,大部分内容是相同的,只是修改了一个比较条件,是不是觉得很无聊,也很无奈,这时你可能会想到加入一个标识来区分颜色和重量。
4、根据flag区分颜色和重量的筛选:
public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {List<Apple> result = new ArrayList<Apple>();for (Apple apple: inventory){if ( (flag && apple.getColor().equals(color)) ||(!flag && apple.getWeight() > weight) ){result.add(apple);}}return result;
}
你可以这么用(但真的很笨拙):
List<Apple> greenApples = filterApples(inventory, "green", 0, true);
List<Apple> heavyApples = filterApples(inventory, "", 150, false);
…
我的天哪,看起来糟糕透了,完全不能理解这个true/false是干嘛用的,其次万一农民伯伯要求你对苹果的不同属性做筛选,比如大小、形状、产地等,怎么办?而且,如果农民要求你组合属性,做更复杂的查询,比如绿色的重苹果,又该怎么办?
好了,废话也讲了一大堆了,下面就进入正题–行为参数化,介绍行为参数化之前还是先给上代码吧,代码如下:
5、使用行为参数化来实现农民的各种需求:
首先,让我们定义一个接口来对选择标准建模:
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 改过之后, filter 方法看起来是这样的:
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;
}
这里值得停下来小小地庆祝一下。这段代码比我们第一次尝试的时候灵活多了,读起来、用起来也更容易!现在你可以创建不同的 ApplePredicate 对象,并将它们传递给 filterApples方法。免费的灵活性!比如,如果农民让你找出所有重量超过150克的红苹果,你只需要创建一个类来实现 ApplePredicate 就行了。你的代码现在足够灵活,可以应对任何涉及苹果属性的需求变更了:
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 方法的行为参数化了!
经过上面的描述相信你已经大概知道什么是行为参数化了,但是,有没有发现什么不足的地方,是不是觉得还是很罗嗦,农民伯伯来一个新需求就需要实现一个类,而且这个类只实现了一个方法,真的很啰嗦啊,怎么办呢?java8之前是提供了一些解决方案的,比如:匿名类,关于匿名类我就不详细描述了,可去查找其它资料,下面只举一个例子来改善上面的代码:
6、使用匿名类实现农民伯伯的需求:
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {//ApplePredicate是先前定义的接口public boolean test(Apple apple){return "red".equals(apple.getColor());}
});
确实是稍微简单了些哈,最起码不用重复的写实现类了,但另外的问题又出来了,可读性差啊,感觉有点绕有木有?好吧,那我们再优化一下好了,怎么优化?使用java8新特性中的 Lambda表达式,代码如下:
7、使用 Lambda表达式来实现农民伯伯的以上需求:
List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
那不光要实现农民伯伯的需求,又引进了其它需求怎么办呢?比如:不是苹果了,变成了萝卜青菜了,这就需要对参数进行抽象化了,代码如下:
public interface Predicate<T>{boolean test(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;
}
List<Apple> redApples = filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);
酷不酷?你现在在灵活性和简洁性之间找到了最佳平衡点,这在Java 8之前是不可能做到的!
本篇主要是介绍Java的行为参数化,因此对Lambda没有进行深入的讲解。以上代码是我从“Java8实战”书中借鉴的,写这篇博客也是为了记录下学习的过程,以后忘了可以翻来看看,写的不好敬请谅解,谢谢。
浅谈Java行为参数化和Lambda表达式相关推荐
- java 中的单元测试_浅谈Java 中的单元测试
单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...
- java的byte php_java_浅谈java的byte数组的不同写法,(由于篇幅原因阐述的不够详 - phpStudy...
浅谈java的byte数组的不同写法 (由于篇幅原因阐述的不够详细科学,不喜勿喷). 经常看到java中对byte数组的不同定义,粗略整理的一下: 一个字节(byte)=8位(bit),"b ...
- 浅谈 Java Printing
浅谈 Java Printing 其实怎么说呢?在写这篇博文之前,我对java printing 可以说是一无所知的.以至于我在敲文字时, 基本上是看着api文档翻译过来的.这虽然看起来非常的吃力, ...
- java接口与类相同不同_浅谈java的接口和C++虚类的相同和不同之处
C++虚类相当于java中的抽象类,与接口的不同之处是: 1.一个子类只能继承一个抽象类(虚类),但能实现多个接口 2.一个抽象类可以有构造方法,接口没有构造方法 3.一个抽象类中的方法不一定是抽象方 ...
- java对象头_浅谈java对象结构 对象头 Markword
概述 对象实例由对象头.实例数据组成,其中对象头包括markword和类型指针,如果是数组,还包括数组长度; | 类型 | 32位JVM | 64位JVM| | ------ ---- | ----- ...
- java执行jar中的main_浅谈java 执行jar包中的main方法
浅谈java 执行jar包中的main方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar 执行后总是运行指定的主方法,如果 jar 中有多个 ...
- 浅谈Java中的Set、List、Map的区别
就学习经验,浅谈Java中的Set,List,Map的区别,对JAVA的集合的理解是想对于数组: 数组是大小固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型),JAVA集合可以存储和操 ...
- Java 函数式编程和 lambda 表达式
为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...
- 浅谈Java网络编程之Socket (2)
<浅谈Java网络编程之Socket (1)>中我们已经和大家说到客户端的网络编程,下面和大家分享的是服务器的实现代码. import java.net.*; import java.io ...
最新文章
- 《程序是怎样跑起来的》第二章
- 云服务器centos怎么还原系统还原,云服务器centos怎么还原系统还原
- 算力用多少买多少,竞享实例太香了
- MTK 驱动开发(54)---MTK-thermal.conf温度参数修改的方法
- iap php,PHP语言之华为应用内支付IAP验签
- elasticsearch映射相关字段定义,属性定义,及动态映射(marvel插件方式)mapping
- 修复VSS 2005
- secoclient在Mac下使用无法上网的解决办法
- 中值滤波(Median filtering)
- Tampermonkey油猴插件
- Premiere 音频视频基本设置
- linux系统新增一块SCSI硬盘并分区挂载到指定目录
- DbVisualizer常见问题解答(ddl标签不存在)
- java课程设计模板_《JAVA课程设计模板.doc
- 一年级古诗风语文知识心田花开汇总
- 中关村科技企业融资缺口700亿 商业银行垂涎
- 《微信小程序案例5》仿小米Lite小程序分类板块-两个纵向滚动区域独立互不影响
- 弘辽科技:淘宝新店提升销量可以吗?怎么提升关键词?
- 网上订餐系统 mysql 数据库设计_网上订餐系统的设计与实现
- iOS学习笔记22 推送通知
热门文章
- 网站漏洞挖掘测试服务
- JAVA SHA-1加密及DES加解密
- 中海达RTK(星移差分)使用指南
- 安装 Oracle 12c
- mpvue使用vuex基本步骤以及如何使用
- 页面置换之最近最久未使用置换算法
- VggNet 论文分析
- 面试指南,求职必看 ! 大学毕业生找工作必备指南!
- 荣耀畅玩4x android 6,华为荣耀4X EMUI4.0回退EMUI3.1教程 安卓6.0降级5.1方法
- 易语言 kernelbase.dll c0000005,【已解决】win7资源管理器停止工作,出错原因是kernelbase.dll...