猪脚:以下内容参考《Java 8 in Action》

发布:https://ryan-miao.github.io/2017/07/15/java8-in-action-2/

源码:github

需求

果农需要筛选苹果,可能想要绿色的,也可能想要红色的,可能想要大苹果(>150g),也可能需要红的大苹果。基于此等条件,编写筛选的代码。

1. 策略模式解决方案

1.1 最直观的做法

首先,已知信息是一筐苹果(List<Apple> inventory),但筛选条件多种多样。我们可以根据不同的条件写不同的方法来达到目的。比如,找出绿色的苹果:

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

同样的,可以编写filterRed, filterWeight等等。但必然出现重复代码,违反软件工程原则Don't repeast yourself。而且,筛选的类也会显得臃肿。

现在,有一种更容易维护,更容易阅读的策略模式来实现这个需求。

1.2 策略模式

由于多种筛选条件的结果都是返回一个boolean值,那么可以把这个条件抽取出来,然后在筛选的时候传入条件。这个筛选条件叫做谓词

创建谓词接口:

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;
}
}
public class AppleRedAndHeavyPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor()) && apple.getWeight() >150;
}
}

筛选的方法:

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

这样,我们就可以根据不同的条件进行筛选了。


List<Apple> inventory = new ArrayList<>();
inventory.add(new Apple("red", 100));
inventory.add(new Apple("red", 200));
inventory.add(new Apple("green", 200));
List<Apple> redHeavyApples = filterApples(inventory, new AppleRedAndHeavyPredicate());
Assert.assertEquals(1, redHeavyApples.size());
Assert.assertEquals(200, redHeavyApples.get(0).getWeight());

以上的代码设计方案几乎是最好理解和扩展的了,当条件发生改变的时候只要增加一个类就可以。但java8提供了更好的选择,一种你只要声明一个接口,具体实现不用管,只有当使用的时候才去关心。

1.3 方法传递

java8提供了把方法当做参数传递的能力。这样,上面的代码就可以这样写:

List<Apple> apples = filterApples(inventory, apple -> "red".equals(apple.getColor()) && apple.getWeight() > 150);
Assert.assertEquals(1, apples.size());
Assert.assertEquals(200, apples.get(0).getWeight());

除了接口声明,不需要实现接口的类。我们只需要传入一个类似匿名内部类的东西,是的,lambda表达式和匿名内部类是可以互相转换的。

如此,我们设计接口的时候只要声明一个接口作为参数,然后再调用的时候把逻辑当做参数传进去。这个在我看来就是传递方法了。就像Javascript,可以把一个方法当做参数。

与之前的设计模式相比,lambda可以不用写那么类。

1.4 新需求

现在,果农需要包装苹果。包装的方式有多种,我将包装的结果打印出来,就是打印的样式也有多种。比如:

A light green apple

或者

An apple of 150g

上面是两种打印方式,按照之前的策略模式需要创建两个类。下面采用lambda来实现。

public interface AppleFormatter {
String format(Apple apple);
} public class AppleOutput{
public static void prettyPrintApple(List<Apple> inventory, AppleFormatter formatter){
for (Apple apple : inventory) {
String format = formatter.format(apple);
System.out.println(format);
}
} public static void main(String[] args){
List<Apple> inventory = new ArrayList<>();
inventory.add(new Apple("red", 100));
inventory.add(new Apple("red", 200));
inventory.add(new Apple("green", 200)); prettyPrintApple(inventory, new AppleFormatter() {
@Override
public String format(Apple apple) {
String characteristic = apple.getWeight()>150?"heavy":"light";
return "A " + characteristic + " " + apple.getColor() + " apple.";
}
}); prettyPrintApple(inventory, apple -> "An apple of " + apple.getWeight() + "g"); }
}

控制台打印:

A light red apple.
A heavy red apple.
A heavy green apple.
An apple of 100g
An apple of 200g
An apple of 200g

如果使用IntelIJ IDEA作为编辑器,那么肯定会忍受不了匿名内部类,因为IDEA会不停的提示你:匿名内部类可以转变为方法参数。

1.5 更普遍的用法

上面的筛选只是针对Apple的,那么是否可以推广开来呢?下面针对List类型抽象化来构造筛选条件。

创建一个条件接口:

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

更新一个更普遍的filter:

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

那么,可能这样用:

public static void main(String[] args) {
List<Apple> appleList = new ArrayList<>();
appleList.add(new Apple("red", 100));
appleList.add(new Apple("red", 160));
appleList.add(new Apple("green", 60)); List<Apple> redApples = filter(appleList, (Apple apple) -> "red".equals(apple.getColor()));
Assert.assertEquals(2, redApples.size()); List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7,8,9);
List<Integer> lessThan4Numbers = filter(numberList, (Integer num) -> num < 4);
Assert.assertEquals(3, lessThan4Numbers.size()); }

1.6 排序

行为参数化的过程掌握后,很多东西就会自然而然的使用了。比如排序。果农需要将苹果按照大小排序呢?

java8中List是有默认方法的:

default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}

其实就是将以前手动排序封装了。那么,苹果的排序就可以传入一个比较器实现:

@Test
public void sort(){
List<Apple> appleList = new ArrayList<>();
appleList.add(new Apple("red", 100));
appleList.add(new Apple("red", 160));
appleList.add(new Apple("green", 60)); appleList.sort((o1, o2) -> o1.getWeight()-o2.getWeight());
}

根据IDEA的提示,进一步:

appleList.sort(Comparator.comparingInt(Apple::getWeight));

这里就涉及了多次行为传参了。后面再说。

1.7 Runnable

多线程Runnable的时候经常会采用匿名内部类的做法:

@Test
public void testRunnable(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("running");
}
}; new Thread(runnable).start();
}

采用lambda行为传参就变为:

@Test
public void testRunnable(){
Runnable runnable = () -> System.out.println("running"); new Thread(runnable).start();
}

小结

本次测试主要理解如下内容:

  • 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
  • 传递代码,就是将行为作为参数传递给方法。

参考

Java8 in action(1) 通过行为参数化传递代码--lambda代替策略模式的更多相关文章

  1. 《Java 8 in Action》Chapter 2:通过行为参数化传递代码

    你将了解行为参数化,这是Java 8非常依赖的一种软件开发模式,也是引入 Lambda表达式的主要原因.行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式.一言以蔽之,它意味 着拿出一个代码 ...

  2. Java通过行为参数化传递代码

    在软件工程中,一个众所周知的问题就是,不管做什么,用户的需求肯定会变.如何应对这样不断变化的需求?理想的状态下,应该把的工作量降到最少.此外,类似的新功能实现起来还应该很简单,而且易于长期维护.行为参 ...

  3. JAVA8 in Action:行为参数化,匿名类及lambda表达式的初步认知实例整理

    import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.functio ...

  4. Liferay7 BPM门户开发之40: Form表单的Action到Render的数据传递

    在Form提交后的变量,很多情况是要展现在jsp页面中,这时Action到Render的变量传递就非常有用. 例如,您在数据库中添加了学生的详细信息. 为了实现这一需求,先创建Form表单(学生的细节 ...

  5. .net中用Action等委托向外传递参数

    原文:.net中用Action等委托向外传递参数      一般我们可以使用ref,out达到向外传递参数目的. Action<T>是一个特殊的委托,除了常规应用.我们还可以用它来实现简单 ...

  6. EF5+MVC4系列(7) 后台SelectListItem传值给前台显示Select下拉框;后台Action接收浏览器传值的4种方式; 后台Action向前台View视图传递数据的四种方式(ViewDate,TempDate,ViewBag,Model (实际是ViewDate.Model传值))

    一:后台使用SelectListItem 传值给前台显示Select下拉框 我们先来看数据库的订单表,里面有3条订单,他们的用户id对应了 UserInfo用户表的数据,现在我们要做的是添加一个Ord ...

  7. 如何优雅的将文件转换为字符串(环绕执行模式&行为参数化&函数式接口|Lambda表达式)

    首先我们讲几个概念: 环绕执行模式: 简单的讲,就是对于OI,JDBC等类似资源,在用完之后需要关闭的,资源处理时常见的一个模式是打开一个资源,做一些处理,然后关闭资源,这个设置和清理阶段类似,并且会 ...

  8. JAVA8初探-让方法参数具备行为能力并引入Lambda表达式

    关于JAVA8学习的意义先来贴一下某网站上的对它的简单介绍:“Java 8可谓Java语言历史上变化最大的一个版本,其承诺要调整Java编程向着函数式风格迈进,这有助于编写出更为简洁.表达力更强,并且 ...

  9. ASP Action函数 如何接收client传递的数据(编辑中。。。)

    参看链接:https://www.cnblogs.com/umlzhang/p/3654486.html 我这里总结一下,Action的参数的来源比较多 1.url 2.路由设定 3.post中的内容 ...

随机推荐

  1. Embedding R-generated Interactive HTML pages in MS PowerPoint(转)

    Usually when I create slide decks these days I used markdown and slidy.  However, I recently was ask ...

  2. What does a Bayes factor feel like?(转)

    A Bayes factor (BF) is a statistical index that quantifies the evidence for a hypothesis, compared t ...

  3. 从计算机语言的发展到我的第一行代码(HelloWorld)

    程序:为了让计算机执行某些操作或解决某个问题而编写的一系列有序指令的集合 算法:解决问题的具体方法和步骤 流程图是算法的一种图形化表示方式. 流程图直观.清晰,更有利于人们设计与理解算法. 它使用一组 ...

  4. .net—— webservice的新建、发布、使用(最全、最简单)【原创】

    网上有很多关于webservice资料,但大部分都不完整,其中还要很大部分是转载的--.这个悲剧了,自己都没试过能不能用就不负责任的转载. 所以今天对webservice的新建.发布.使用最一个全面. ...

  5. css中那些容易被我们程序猿所忽略的选择器

    css中那些容易被我们程序猿所忽略的选择器 作为一个程序猿,我们可能会遇到这样的问题,假如:有5个li,要求给第三个li设置背景颜色怎么办?有人会说,用JS啊,循环遍历出第三个不就行了.但是,用JS解 ...

  6. Unity应用架构设计(11)——一个网络层的构建

    对于客户端应用程序,免不了和远程服务打交道.设计一个良好的『服务层』能帮我们规范和分离业务代码,提高生产效率.服务层最核心的模块一定是怎样发送请求,虽然Mono提供了很多C#网络请求类,诸如WebCl ...

  7. eChart学习笔记

    eChart的html代码很简单,给个容器,定好宽高就可以了 1 <div class="container-fluid"> 2 <div class=" ...

  8. 关于dubbo分享

    一.dubbo服务是基于zookeeper提供服务.提供消费 1.Zookeeper的作用: zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是i ...

  9. Linux crontab定时执行任务命令格式与详细例子

    基本格式 : * * * * * command 分 时 日 月 周 命令 第1列表示分钟1-59 每分钟用*或者 */1表示 第2列表示小时1-23(0表示0点) 第3列表示日期1-31 第4列表示 ...

  10. python web -- django

    一. 安装 django $ pip install django (env)$ python >> import django >> django.VERSION >& ...