行为参数化:帮助你处理频繁变更的需求的一种软件开发模式,即可以将代码块作为参数传递给另一个方法,稍后再去执行它。此时这个方法的行为就基于那块代码被参数化了。
行为参数化就是让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为。
特点:
1)可以对列表中的每个元素做“某件事”
2)可以在列表处理完后做“另一件事”
3)遇到错误时可以做“另外一件事”
示例1:实现从一个列表中筛选出绿色苹果?
实现方式有如下几种:
方式一:遍历筛选
public static List<Apple> filterGreenApples(List<Apple> list) {
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if("green".equals(apple.getColor()) result.add(apple);
}
return result;
}
此种方式可以实现筛选绿苹果的要求,但是如果想要筛选更多颜色的苹果就不能满足需求了,必须复制更多的filterGreenApples方法或者在循环体中增加各种if条件判断来解决,带来的问题是代码重复且复用率低,维护困难。
方式二:把颜色作为参数,适应不同颜色
public static List<Apple> filterApplesByColor(List<Apple> list, String color) {
List<Apple> result = new ArrayList<>();
for (Apple apple: list){
if (apple.getColor().equals(color)) result.add(apple);
}
return result;
}
此种方式可以解决颜色筛选问题,但是如果想要按重量筛选该如何处理呢?若将上述方法复制一份改为按重量过滤,则会带来代码的重复,若想更改遍历方式提升性能,就必须更改所有的方法,代价太大。
方式三:尝试对你能想到的每个属性做筛选
public static List<Apple> filterApples(List<Apple> list, String color, int weight, boolean flag) {
List<Apple> result = new ArrayList<>();
for (Apple apple: list){
if ((flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight))
result.add(apple);
}
return result;
}
此方式虽然可以解决上述需求,但是实现效果很差,并不推荐使用。如果进行组合属性或更复杂的查询,将会有多个重复的filter方法或是非常复杂的参数,维护起来会非常困难。
方式四、对选择标准建模,根据抽象条件筛选(策略模式)
你需要一种比添加很多参数更好的方法来应对变化的需求,一种可能的解决方案是对你的选择标准建模:如根据Apple的某些属性来返回一个boolean值。我们把它称为谓词(即一个返回boolean值的函数)。
定义一个接口来对选择标准建模:
public interface ApplePredicate{
boolean test (Apple apple);
}
此时你就可以用ApplePredicate的多个实现代表不同的选择标准了,比如:
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){return apple.getWeight() > 150;} //筛选重量
}
public class AppleGreenColorPredicate implements ApplePredicate{
public boolean test(Apple apple){return "green".equals(apple.getColor());} //筛选颜色
}
使用ApplePredicate改进筛选方法:
public static List<Apple> filterApples(List<Apple> list, ApplePredicate p){
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(p.test(apple)) result.add(apple); //谓词对象封装了测试苹果的条件
}
return result;
}
此时代码看起来更加简洁灵活和清晰易用了,可以方便的根据不同的条件应对不同的需求变更。例如:找出所有重量超过150克的红苹果。
public class AppleRedAndHeavyPredicate implements ApplePredicate{
public boolean test(Apple apple){
return "red".equals(apple.getColor()) && apple.getWeight() > 150;
}
}
List<Apple> redAndHeavyApples = filterApples(inventory, new AppleRedAndHeavyPredicate());
filterApples方法的行为取决于你通过ApplePredicate对象传递的代码,即把filterApples方法的行为参数化了。
方式五、使用匿名类
上述方式在需要把新的行为传递给filterApples方法时,你不得不声明好几个实现ApplePredicate接口的类,然后实例化多个只会用到一次的ApplePredicate对象,让代码看起来比较啰嗦。如何改进?Java有一个机制称为匿名类,它可以让你同时声明和实例化一个类,帮你改善代码变得更简洁。
通过创建一个用匿名类实现ApplePredicate的对象,重写筛选:
List<Apple> redApples = filterApples(inventory, new ApplePredicate() { //直接内联参数化 filterapples方 法的行为
public boolean test(Apple apple){ return "red".equals(apple.getColor());}
});
匿名类的缺点:比较笨重,占用空间较大,其次代码不容易理解;
答案:5,this指的是包含它的Runnable,而不是外面的类MeaningOfThis。
方式六、使用Lambda表达式(推荐)
用Lambda表达式重写上述代码:
List result = filterApples(inventory,(Apple apple) -> "red".equals(apple.getColor()));
此方式代码非常简洁,看起来更像问题陈述本身,完全解决了匿名类的啰嗦问题;
方式七、将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;
}
示例2:用Comparator来排序
在Java 8中,List自带了一个sort方法(或用Collections.sort)。sort的行为可以用java.util.Comparator对象来参数化:
public interface Comparator<T> {public int compare(T o1, T o2);}
你可以创建Comparator的实现,用sort方法表现出不同的行为。比如,你可以使用匿名类,按照重量升序对库存排序:
1>普通方式
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
2>Lambda方式
inventory.sort( (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
示例3:用Runnable执行代码块
Runnable接口表示一个要执行的代码块,代码不会返回任何结果 ;
public interface Runnable{public void run();}
1>普通方式
Thread t = new Thread(new Runnable() {
public void run(){
System.out.println("Hello world");
}
});
2>Lambda方式
Thread t = new Thread(() -> System.out.println("Hello world"));
以上示例摘自《Java8实战》