在软件工程中,一个众所周知的问题就是,不管做什么,用户的需求肯定会变。
如何应对这样不断变化的需求?理想的状态下,应该把的工作量降到最少。此外,类似的新功能实现起来还应该很简单,而且易于长期维护。
行为参数化就是可以帮助处理频繁变更的需求的一种软件开发模式。一言以蔽之,它意味着拿出一个代码块,把它准备好却不去执行它。这个代码块以后可以被程序的其他部分调用,这意味着可以推迟这块代码的执行。

以筛选苹果为例,逐步改进代码,来展示一些让代码更灵活的最佳做法。

需求:筛选绿色苹果

1.第一次尝试:为了实现筛选绿苹果,for循环筛选绿苹果

List<Apple> inventory = Arrays.asList(new Apple(80, "green"), new Apple(155, "green"), new Apple(120, "red"));
public class Apple {
private int weight;
private String color;
  // get setter ...
}
List<Apple> apples = filterGreenApples(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;
}

需求变化:筛选其他颜色的苹果

2.第二次尝试:把颜色作为参数,筛选对应颜色

List<Apple> apples = filterApplesByColor(inventory,"red");

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

需求变化:刷选颜色加重量

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

public static List<Apple> filterApples(List<Apple> inventory, String color,int weight) {
//...

这个解决方案还是不能很好地应对变化的需求。

4.第四次尝试:根据抽象条件筛选

List<Apple> apples = filterApples(inventory, new AppleColorPredicate());

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;
} interface ApplePredicate {
public boolean test(Apple a);
} static class AppleWeightPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
} static class AppleColorPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}

附:刚做的这些和“策略设计模式”相关,它让定义一族算法,把它们封装起来(称为“策略”),然后在运行时选择一个算法。在这里,算法族就是ApplePredicate,不同的策略就是AppleHeavyWeightPredicate和AppleGreenColorPredicate。 但是,该怎么利用ApplePredicate的不同实现呢?需要filterApples方法接受ApplePredicate对象,对Apple做条件测试。这就是行为参数化:让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为。 要在我们的例子中实现这一点,要给filterApples方法添加一个参数,让它接受ApplePredicate对象。这在软件工程上有很大好处:现在把filterApples方法迭代集合的逻辑与要应用到集合中每个元素的行为区分开了。

请注意,在这个例子中,唯一重要的代码是test方法的实现;正是它定义了filterApples方法的新行为。由于该filterApples方法只能接受对象,所以必须把代码包裹在ApplePredicate对象里。的做法就类似于在内联“传递代码”,因为是通过一个实现了test方法的对象来传递布尔表达式的。

这种行为参数化的好处在于可以把迭代要筛选的集合的逻辑与对集合中每个元素应用的行为区分开来。这样可以重复使用同一个方法,给它不同的行为来达到不同的目的

5. 第五次尝试:使用匿名类

List<Apple> redApples = filterApples(inventory, new ApplePredicate() { // 直接内联参数化filterapples方法的行为
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});

附:匿名类和熟悉的Java局部类(块中定义的类)差不多,但匿名类没有名字。它允许同时声明并实例化一个类。换句话说,它允许随用随建。

6.第六次尝试:使用Lambda表达式

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

7.第七次尝试:将List类型抽象化

在通往抽象的路上,我们还可以更进一步。目前,filterApples方法还只适用于Apple。还可以将List类型抽象化,从而超越眼前要处理的问题:

public interface Predicate<T> {
boolean test(T t);
} public static <T> List<T> filter(List<T> list, Predicate<T> p) { //引入类型参数T
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()));
System.out.println(redApples); List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);
System.out.println(evenNumbers);

参考:java8实战第二章

Java通过行为参数化传递代码的更多相关文章

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

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

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

    [TOC] 猪脚:以下内容参考<Java 8 in Action> 需求 果农需要筛选苹果,可能想要绿色的,也可能想要红色的,可能想要大苹果(>150g),也可能需要红的大苹果.基于 ...

  3. Java 中的值传递和参数传递

    Java中没有指针,所以也没有引用传递了,仅仅有值传递不过可以通过对象的方式来实现引用传递 类似java没有多继承 但可以用多次implements 接口实现多继承的功能 值传递:方法调用时,实际参数 ...

  4. Android使用JNI实现Java与C之间传递数据(转)

    介绍Java如何将数据传递给C和C回调Java的方法.  java传递数据给C,在C代码中进行处理数据,处理完数据后返回给java.C的回调是Java传递数据给C,C需要用到Java中的某个方法,就需 ...

  5. 在Java中直接调用js代码(转载)

    http://blog.csdn.net/xzyxuanyuan/article/details/8062887 JDK1.6版添加了新的ScriptEngine类,允许用户直接执行js代码. 在Ja ...

  6. 在Java中直接调用js代码

    JDK1.6版添加了新的ScriptEngine类,允许用户直接执行js代码. 在Java中直接调用js代码 不能调用浏览器中定义的js函数,会抛出异常提示ReferenceError: “alert ...

  7. java到底是引用传递还是值传递?

    今天我们来讲讲一个在学习中容易误解的问题,面试中也偶尔问到,java方法调用时到底是值传递还是引用传递? 首先,请大家来做一个判断题,下面的3个问题是否描述正确 1. java基本数据类型传递是值传递 ...

  8. 为什么说Java中只有值传递

    本文转载自公众号 Hollis 对于初学者来说,要想把这个问题回答正确,是比较难的.在第二天整理答案的时候,我发现我竟然无法通过简单的语言把这个事情描述的很容易理解,遗憾的是,我也没有在网上找到哪篇文 ...

  9. 一道笔试题来理顺Java中的值传递和引用传递

      题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = ...

随机推荐

  1. mongodb移除分片和添加分片(转)

    首先我们要移除的分片之后再次添加此分片时会出现添加失败的情况,需要在添加的分片上登录进行删除此分片之前数据库的历史数据比如testdb,删除分片上的数据库之后就可重新添加此分片到mongos中 1.执 ...

  2. 米勒罗宾素数检测(Miller-Rabin)

    适用范围:较大数的较快素性判断 思路: 因为有好的文章讲解具体原理(见参考文章),这里只是把代码的大致思路点一下,读完了文章如果还有些迷糊,可以参考以下解释 原理是费马小定理:如果p是素数,则a^(p ...

  3. requireJS的基本使用

    requireJS的基本使用 一.总结 一句话总结: requireJS是js端模块化开发,主要是实现js的异步加载,和管理模块之间的依赖关系,便于代码的编写和维护 1.页面加载的js文件过多的缺点是 ...

  4. sql分割函数

    drop function [dbo].[f_split] create function [dbo].[f_split] ( ),--需要分割的字符串(例如:1,2,3,4,5 我|和|你) )-- ...

  5. 《exception》第九次团队作业:Beta冲刺与验收准备(第三天)

    一.项目基本介绍 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 Exception 作业学习目标 1.掌握软件黑盒测试技术:2.学会编制软件项目 ...

  6. 项目Alpha冲刺(团队)-总结篇

    格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(团队)-代码规范.冲刺任务与计划 团队名称:为了交项目干杯 作业目标:描述项目预期计划.现实进展.过程体会.组员分工 ...

  7. intellij idea 搜索快捷键

    Ctrl+N按名字搜索类 1 相当于eclipse的ctrl+shift+R,输入类名可以定位到这个类文件 2 就像idea在其它的搜索部分的表现一样,搜索类名也能对你所要搜索的内容多个部分进行匹配 ...

  8. SpringSecurity 整合 JWT

    项目集成Spring Security(一) 在上一篇基础上继续集成 JWT ,实现用户身份验证. 前言 前后端分离项目中,如果直接把 API 接口对外开放,我们知道这样风险是很大的,所以在上一篇中我 ...

  9. BZOJ 4771: 七彩树 可持久化线段树+树链的并

    这个思路挺有意思的 ~ 利用树链的并来保证每个颜色只贡献一次,然后用可持久化线段树维护 code: #include <set> #include <cstdio> #incl ...

  10. 博客之旅的开始-----什么是Python ? ? ?

    1 .到底什么是Python?你可以在回答中与其他技术进行对比(也鼓励这样做). 答案下面是一些关键点: Python是一种解释型语言.这就是说,与C语言和C的衍生语言不同,Python代码在运行之前 ...