猪脚:以下内容参考《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 {@Overridepublic boolean test(Apple apple) {return "green".equals(apple.getColor());}
}
public class AppleHeavyWeightPredicate implements ApplePredicate {@Overridepublic boolean test(Apple apple) {return apple.getWeight() > 150;}
}
public class AppleRedAndHeavyPredicate implements ApplePredicate {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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();
}

小结

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

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

参考

  • Java 8 in action

唯有不断学习方能改变! -- Ryan Miao

Java8 in action(1) 通过行为参数化传递代码--lambda代替策略模式相关推荐

  1. java8实战一:通过行为参数化传递代码

    通过行为参数化传递代码 如何对你的代码加以改进,从而更灵活地适应不断变化的需求? 行为参数化就是可以帮你处理频繁变更的需求的一种软件开发模式. 一言以蔽之,它意味 着拿出一个代码块,把它准备好却不去执 ...

  2. 使用行为参数化传递代码

    在软件开发过程中,有一个比较头疼的问题,就是用户的需求会经常改变,这也使得我们总是要不断的去修改代码,从而浪费了不少时间和精力.如何应对不断变化的需求?如何降低软件开发的工作量?java8提出了一种新 ...

  3. java 参数传递实体类_Java8实战——通过行为参数化传递代码

    1.初试牛刀:筛选绿苹果 第一个解决方案可能是下面这样的: public static List filterGreenApples(List inventory){ List result=new ...

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

    行为参数化就是可以帮你处理频繁变更的需求的一种软件开发模式. 一言以蔽之,它意味着拿出一个代码块,把它准备好却不去执行它.这个代码块以后可以被你程序的其他部分调用, 这意味着你可以推迟这块代码的执行. ...

  5. 如何理解jdk8通过行为参数化传递代码

    行为参数化可以帮助你处理频繁变更需求的一种软件开发模式. public static List<Apple> filterApples(List<Apple> inventor ...

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

    文章目录 什么是行为参数化 理解行为参数化的经典案例--苹果的故事 1 定义苹果对象类 2 根据颜色筛选苹果(值参数化) 3 使用匿名内部类进行优化 4 使用Lambda表达式继续优化 5 小结 参考 ...

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

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

  8. 《软件功能测试自动化实战教程》—第6章6.4节Action测试输入的参数化

    本节书摘来自异步社区<软件功能测试自动化实战教程>一书中的第6章6.4节Action测试输入的参数化,作者51Testing网 , 周焕来 , 贾海涛,更多章节内容可以访问云栖社区&quo ...

  9. java8 策略模式_Java 8中的策略模式

    java8 策略模式 这是两个有关如何使用Java 8功能样式以及Cyclops模式匹配和Hamcrest库来实现策略模式设计的示例. PrintDependingOnInput方法是一种策略,该策略 ...

最新文章

  1. Web App、Hybrid App与Native App的设计差异
  2. 关于owner group others的测试
  3. Linux2.6内核 -- 编码风格(2)
  4. mybatis对mapper.xml的解析(一)
  5. Python数据结构与算法(第二天)
  6. docker 安装openmaptiles
  7. emoji表情过滤处理
  8. 动态与代理AOP--01【代理的作用与概念】【动态代理与AOP】
  9. timequest静态时序分析学习笔记之工具使用
  10. EIP-1559性能测试:22小时共处理6426个区块,平均每个区块4100万Gas
  11. Pytesseract-windows安装及初步使用
  12. spark 部署安装
  13. 3Dmax移动,旋转,缩放图标不显示
  14. [XMAN2018排位赛]通行证
  15. python调用mysql并在前台做数据展示
  16. sqlserver创建函数后提示对象名无效
  17. android 数据线有几种,不止是安卓和苹果线,手机数据线原来还有这几种!
  18. java结巴分词如何提高运行速度_结巴分词 java 高性能实现,优雅易用的 api 设计,性能优于 huaban jieba 分词...
  19. 【机器学习】决策树与随机森林模型
  20. 玩转Redis-HyperLogLog统计微博日活月活

热门文章

  1. 自由缩放属性-resize(禁止textarea的自由缩放尺寸功能)
  2. 解决filezilla中无法显示中文的文件名
  3. 【配置】Spring Struts配置信息
  4. 数据结构笔记(二十一)--二叉树的遍历
  5. Linux学习笔记之权限与命令之间的关系(重要)及文件与目录知识总结
  6. 2020华为软件精英挑战赛-有向图找环
  7. 用单片机测量流体流速的_沟渠流量测量系统宝山哪家质量好广州顺仪品牌
  8. java给xml加入值,怎么用java读取XML文件里的值并加入jsp页面中的下拉列表中
  9. docker 网络配置_Kafka的AWS Docker网络设置
  10. 如何在自己开发的日程管理页面插入提醒功能_给大家推荐一款高颜值的日程管理类APP...