应对不断变化的需求
在实际的工作中我们会将现实问题抽象成对象并对其进行处理,比如需要对一堆颜色和重量不同的苹果进行过滤分类。
1、苹果实体类
public class Apple { // 颜色 private String color; // 重量 private Integer weight; // Getter and Setter}
2、过滤方法
public static Listfilter(List appleList, String color, int weight) { // 符合条件的苹果集合 List result = new ArrayList<>(); for (Apple apple : appleList) { // 如果颜色和重量符合条件就存入 if (color.equalsIgnoreCase(apple.getColor()) && weight == apple.getWeight()) { result.add(apple); } } return result; }
通过定制过滤方法,比如后期苹果可能会有其他的属性,是否成熟、产地等。我们可以在过滤方法的入参加上对应的属性并在内部进行判断。这就是通过修改过滤方法来 应对不断变化的需求
。但这样有其局限性,如果需求不断地更改,那么就需要重写很多相似的方法。这违背了
DRY(Don't Repeat Yourself)
的软件工程原则。
行为参数化
我们其实可以通过标准建模来定义一个过滤接口,让其比重写很多次过滤方法更好地 应对不断变化的需求
。
1、建立苹果谓词接口
// predicate:谓词,即一个返回 boolean 值的接口public interface ApplePredicate { boolean test(Apple apple);}
2、运用策略模式思想来构建具体算法(策略)实现
public class AppleColorPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { // 选出绿色的苹果 return "green".equalsIgnoreCase(apple.getColor()); }}public class AppleWeightPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { // 选出重量大于1的苹果 return 1 < apple.getWeight(); }}
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。
- 定义了一族算法(业务规则);
- 封装了每个算法;
- 这族的算法可互换代替(interchangeable)——
我们可以将 AppleColorPredicate
和 AppleWeightPredicate
看作是 过滤方法
的不同行为,需要 过滤方法
接收 ApplePredicate
对象对苹果进行过滤。这就是 行为参数化
:让方法接收多种行为(或策略)作为参数,并在内部使用,来完成不同的行为。
1、修改过滤方法让其能够接收苹果谓词接口对象
public static Listfilter(List appleList, ApplePredicate applePredicate) { // 符合条件的苹果集合 List result = new ArrayList<>(); for (Apple apple : appleList) { // 如果符合条件就存入 if (applePredicate.test(apple)) { result.add(apple); } } return result; }
2、调用过滤方法进行过滤
public class Main { public static void main(String[] args) { ListappleList = new ArrayList<>(); Apple apple = new Apple(); apple.setColor("red"); apple.setWeight(1); appleList.add(apple); apple = new Apple(); apple.setColor("green"); apple.setWeight(2); appleList.add(apple); List result = filter(appleList, new AppleWeightPredicate()); }}
result 中就会只有重量大于1的苹果集合了。行为参数化
的好处在于我们可以把过滤的逻辑 boolean test()
与应用过滤的行为 public static List<Apple> filter()
解耦。这样在需求不断更改时,只需要新增 ApplePredicate
实现再调用就行。
对付啰嗦
然而按照以上方式使用 ApplePredicate
依然有一个问题,那就是我们还是得不断地新增 ApplePredicate
的实现。本质上只是把重写过滤方法的代价转移到了新增谓词实现上。这个时候我们可以换一个思路出发,使用 匿名类
来随用随建谓词实现。
使用匿名类实现谓词接口
Listresult = filter(appleList, new ApplePredicate() { @Override public boolean test(Apple apple) { // 选出绿苹果且重量为2 return "green".equalsIgnoreCase(apple.getColor()) && 2 == apple.getWeight(); } });
现在,我们只需要每次去匿名实现谓词接口就行,然而这样的写让人觉得很臃肿,而且看起来很让人费解。接下来看看 Lambda 是怎么让其变得简洁又友好的。
通过 Lambda 简化匿名实现
Listresult = filter(appleList, (Apple apple1) -> "green".equalsIgnoreCase(apple1.getColor()) && 2 == apple1.getWeight());
是不是简洁得有点看不懂了?没关系,先细细品味,下一章我们会详细了解 Lambda。
我们还可以进一步抽象。目前 ApplePredicate
还只适用于苹果,而我想要其他对象进行过滤呢?可以使用泛型来定义需要处理的对象。
1、修改 ApplePredicate 成 Predicate
public interface Predicate{ boolean test(T t);}
2、修改过滤方法
public staticList filter(List list, Predicate predicate) { // 符合条件的集合 List result = new ArrayList<>(); for (T t : list) { // 如果符合条件就存入 if (predicate.test(t)) { result.add(t); } } return result; }
这样我们就能将过滤方法用在其他对象上了。下一章我们会更加深入地理解 Lambda 是什么,能干什么。
Java 8 实战 第二章 通过行为参数化传递代码 读书笔记
这是我第一篇文章,欢迎加入咖啡馆的春天(338147322)。