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

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

需求:筛选绿色苹果

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实战第二章

最新文章

  1. 让 FreeBSD 和 Gentoo Linux 在 ZFS 存储卷上共存
  2. 团队开发——冲刺2.e
  3. form表单验证和事件、正则表达式
  4. HDU-敌兵布阵
  5. SQL SERVER 安全性体系
  6. [ios][swift]使用swift闭包进行viewcontroller反向传值
  7. javascript触摸事件touch使用
  8. Java学习----this和super(在继承中)
  9. 根据获取Enum名获取对应的值通用方法(仅限值为int的)
  10. 实现基本TCP套接字客户端
  11. 《JAVA程序设计》_第八周学习总结
  12. python基础1之 由来、种类、优缺点、安装环境
  13. typeScript 学习
  14. 设置mysql 事务锁超时时间 innodb_lock_wait_timeout
  15. findHomography(src_points, dst_points, CV_RANSAC)
  16. 关于游览器 cookie的操作类
  17. winSocket编程(十)完成端口
  18. Tcp/Ip 三次握手与四次挥手
  19. Ant.OutputIsUnreadableCode
  20. spring jpa exists

热门文章

  1. Linux操作系统故障排除之Grub信息被删除
  2. hdu3486Interviewe(二分是错的)(ST算法RMQ + 判定上下界枚举)
  3. Alpha冲刺(10/10)——追光的人
  4. 当调用对象中不存的方法、属性时,__getattr__的应用场景
  5. linux ssh tunnel
  6. .Net Core 遇到 “&#39;windows-1252&#39; is not a supported encoding name.”
  7. window对象方法(open和close)
  8. 用 ThreadPoolExecutor/ThreadPoolTaskExecutor 线程池技术提高系统吞吐量(附带线程池参数详解和使用注意事项)
  9. Linux桌面最轻量的Dock之Plank介绍
  10. Java两个数的和