功能模块的行为参数化改造

功能模块的行为参数化改造

假设最开始,我们的需求是:从一堆苹果List中筛选出绿色的苹果。我们会写出如下代码:

1
2
3
4
5
6
7
8
9
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>() ;
for(Apple a : inventory) {
if ("green".equals(a.getColor() ) ) {
result.add(a) ;
}
}
return result;
}

但此时需求变更了:不止要筛选出绿苹果,还要筛选出红苹果、粉苹果、黑苹果……
每个颜色的苹果写一个函数吗?当然不,因为这样违背了软件开发的Don't Repeat Yourself原则。
我们需要对功能模块进行抽象化扩展,将颜色作为参数传入方法中。

对方法进行抽象化扩展

我们写出了能够通过颜色筛选苹果的代码模块:

1
2
3
4
5
6
7
8
9
public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
List<Apple> result = new ArrayList<>() ;
for(Apple a : inventory) {
if (a.getColor().equals(color) ) {
result.add(a) ;
}
}
return result;
}

但此时需求又变更了:不止要通过颜色筛选,还要通过重量筛选。
这时候我们怎么办?再写一个函数,通过重量筛选?还是直接修改原有函数,使其既能通过颜色筛选,也能通过重量筛选?
我们会发现,不管怎么做,都会违背Don't Repeat Yourself原则,你的代码会存在很多冗余。
这时候,我们对功能模块进行行为参数化改造,使其能接受行为作为参数,满足不同的筛选需求。

行为参数化

首先我们了解一下什么是谓词:

谓词(Predicate):
谓词是一种返回boolean值的函数,比如下方代码中的test()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//定义一个谓词接口,包含test()函数,传入一个Apple对象,返回true或false
public interface ApplePredicate{
boolean test(Apple apple) ;
}

//ApplePredicate具体实现类1,传入Apple对象,返回重量是否超过150
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}

//ApplePredicate具体实现类2,传入Apple对象,返回是否是绿苹果
public class AppleGreenColorPredicate implements ApplePredicate{
public boolean test(Apple apple) {
return "green".equals(apple.getColor() ) ;
}
}

//筛选苹果类
public class FilteringApples{

public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(new Apple(80,"green"), new Apple(155,"green"), new Apple(120,"red") );
List<Apple> heavyApples = filterApples(inventory, new AppleHeavyWeightPredicate() );
List<Apple> greenApples = filterApples(inventory, new AppleGreenColorPredicate() );
}

//筛选苹果函数
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = new ArrayList<>() ;
for (Apple a : inventory) {
if (p.test(a) ) {
result.add(a) ;
}
}
return result;
}
}

我们写出了通用的功能模块,可以根据传入的行为来进行筛选苹果,但这时,我们会发现AppleHeavyWeightPredicateAppleGreenColorPredicate两个类不仅有大量相同的代码,而且两个类除了test()方法被使用了之外毫无用处。
能不能直接使用方法,而不让方法依托于某个类呢?这时,我们需要对代码进行Lambda改造

Lambda改造

我们直接删除AppleHeavyWeightPredicateAppleGreenColorPredicate两个类,在代码中直接通过Lambda表达式进行匿名调用test()方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//定义一个谓词接口,包含test()函数,传入一个Apple对象,返回true或false
public interface ApplePredicate{
boolean test(Apple apple) ;
}

//筛选苹果类
public class FilteringApples{

public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(new Apple(80,"green"), new Apple(155,"green"), new Apple(120,"red") );
//Lambda改造,匿名调用test()方法
List<Apple> heavyApples = filterApples(inventory, (Apple a) -> a.getWeight > 150);
List<Apple> greenApples = filterApples(inventory, (Apple a) -> "green".equals(a.getColor() ) );
}

//筛选苹果函数
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = new ArrayList<>() ;
for (Apple a : inventory) {
if (p.test(a) ) {
result.add(a) ;
}
}
return result;
}
}

到这一步,我们已经可以提供一个通用的筛选苹果的功能模块了,这时候如果需求变成了:不仅要能筛选苹果,其他所有事物都要能进行筛选。
怎么办呢?我们就要对整个接口进行抽象化扩展

对接口进行抽象化扩展

我们修改整个谓词接口和筛选函数,使之能够满足筛选任何对象的需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义一个谓词接口,包含test()函数,传入任何,返回true或false
public interface Predicate<T>{
boolean test(T t) ;
}

//筛选函数
public static List<T> filterApples(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>() ;
for (T t : list) {
if (p.test(t) ) {
result.add(t) ;
}
}
return result;
}

这样我们就改造好了一个能筛选任何对象的功能模块。

-------------本文结束感谢您的阅读-------------

本文标题:功能模块的行为参数化改造

文章作者:DragonBaby308

发布时间:2019年05月31日 - 14:33

最后更新:2019年09月22日 - 10:09

原始链接:http://www.dragonbaby308.com/Java8-Behavior-Parameterization/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%