行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式。一言以蔽之,它意味着拿出一个代码块,把它 准备好却不去执行它。这个代码块以后可以被你程序的其他部分调用,这意味着你可以推迟这块代码的执行。例如,你可以将代码块作为参数传递给另一个方法,稍后再去执行它。这样, 这个方法的行为就基于那块代码被参数化了。
行为参数化
拿上一文中的Apple举例,需要根据Apple的某些属性(比如它是绿色的吗?重量超过150克吗?)来返回一个boolean值。我们把它称为谓词(即一个返回boolean值的函数)。
- 第一次尝试:匿名函数
一个很好的尝试是通过匿名类进行筛选:
public interface ApplePredicate {
boolean test(Apple apple);
}
public class Filter {
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;
}
public static void main(String[] args) {
List<Apple> inventory=new ArrayList<>();
inventory.add(new Apple("red",1));
filterApples(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "red".equalsIgnoreCase(apple.getColor());
}
}).forEach(item->{//不要在意此处的lambda表达式,用于打印
System.out.println(item.toString());
});
}
}
GUI应用程序中经常使用匿名类来创建事件处理器对象。
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
System.out.println("Woooo a click!!");
}
});
但匿名类还是不够好。第一,它往往很笨重,因为它占用了很多空间。第二,很多程序员觉得它用起来很让人费解。
- 第二次尝试:使用lambda表达式
所以使用Lambda表达式重写为如下形式:
public class Filter {
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;
}
public static void main(String[] args) {
List<Apple> inventory=new ArrayList<>();
inventory.add(new Apple("red",1));
filterApples(inventory, (Apple a)->"red".equalsIgnoreCase(a.getColor()))
.forEach(item->{
System.out.println(item.toString());
});
}
}
- 第三次尝试:将List类型抽象化
实际上,还可以进一步简化:
public interface Predicate<T> {
boolean test(T e);
}
public class Filter {
static <T> List<T> filterAllFruits(List<T> inventory,
Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T e : inventory) {
if (p.test(e)) {
result.add(e);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> inventory = new ArrayList<>();
inventory.add(new Apple("red", 1));
filterAllFruits(inventory, (Apple a) -> "red".equals(a.getColor()))
.forEach(item -> {
System.out.println(item.toString());
});
}
}
实用的例子
你现在已经看到,行为参数化是一个很有用的模式,它能够轻松地适应不断变化的需求。这种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创建的行为(例如对Apple的不同谓词)将方法的行为参数化。
- 用Comparator来排序
对集合进行排序是一个常见的编程任务。比如,你的那位农民朋友想要根据苹果的重量对库存进行排序,或者他可能改了主意,希望你根据颜色对苹果进行排序。
inventory.sort(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getWeight().compareTo(o2.getWeight());
}
});
用Lambda:
inventory.sort((Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight()));
- 用Runnable执行代码块
Thread t = new Thread(new Runnable() {
public void run(){
System.out.println("Hello world");
}
});
Lambda:
Thread t=new Thread(()->System.out.println("Hello world"));
- GUI事件处理
Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
Lambda:
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));