1. 应对不断变化的需求
该篇将讲述如何应对不断变化的需求,逐渐优化代码,以展示代码更灵活的最佳做法。
1.1 初试牛刀:筛选红苹果
public static List<Apple> filterRedApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if("red".equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
实现一个filter方法对List做遍历,筛选出所有的红苹果,那现在农民又有了新的想法,他想筛选出绿苹果,那是不是要再写一个filterGreenApple(List<Apple> inventory)来实现呢?那如果还想筛选其他颜色呢,显然我们不能每次都定义一个新的方法,这个时候我们可以把颜色color作为参数传递进去,这样就不用写重复的代码。
1.2 将颜色作为参数传递
public static List<Apple> filterColorApples(List<Apple> inventory, String color) {
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(apple.getColor().equals(color)){
result.add(apple);
}
}
return result;
}
这样做你就能筛选出你想要的的任意颜色,但这是,农民又想筛选出不同重量的苹果,你肯定会想将重量作为参数传递给方法filterWeightApples(List<Apple> inventory, String color)。但是如果有多个条件,就会考虑将尽可能多的条件包含在一个过滤发放中。
1.3 对你想到的每一个属性做筛选
public static List<Apple> filterColorApples(List<Apple> inventory, String color, int weight, boolean flag) {
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(flag && apple.getColor().equals(color) || !flag && apple.getWeight() > weight){
result.add(apple);
}
}
return result;
}
显然这样的代码是糟糕的,因为用户根本不知道true和false代表什么意思,并且想要将所有属性都包含进去显然是不现实的,万一又对形状,大小有要求呢?
从下面开始我们就会介绍行为参数化怎么更简便的实现筛选。
1.4 行为参数化 - 根据抽象条件筛选
public interface ApplePredicate{ //定义接口
public boolean test(Apple apple);
}
public class AppleRedPredicate implements ApplePredicate{
@override
public boolean test(Apple apple){
return "red".equals(apple.getColor());
}
}
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p){
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(p.test()){
result.add(apple);
}
}
return result;
}
ApplePredicate p = new AppleRedPredicate();
filterApples(inventory, p) //筛选出红苹果
这样显然比之前要简单许多,公用同一个filter方法,将筛选的数据集与筛选的逻辑分开来,但是每增加一次筛选就要实现一次ApplePredicate接口,显然有些麻烦,这个时候就想起了匿名类。
1.5 使用匿名类
public interface ApplePredicate{
public boolean test(Apple apple);
}
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;
}
filterApples(inventory, new ApplePredicate(){
@override
public boolean filterRedApple(Apple, apple){
return "red".equals(apple.getColor());
}
})
显然使用内部类,不用再显示定义实现接口的类,但是仍有很多模板代码,其实我们关心的只有return的那一行。如果只需要传递不同的部分就好了。
1.6 使用Lambda表达式
public interface ApplePredicate{
public boolean test(Apple apple);
}
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;
}
filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
除了Lambda表达式之外,我们还可以使用方法引用来传递代码。但是我们之前提到过对于复杂的代码块与匿名函数更方便,简单的代码块使用Lambda更方便。下面展示匿名函数:
public static boolean isRedApple(Apple apple){
return "red".equals(apple.getColor());
}
public interface ApplePredicate{
public boolean test(Apple apple);
}
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;
}
filterApples(inventory, Apple::isRedApple);
现在我们已经得到了我们想要的,只传递必要的代码块,下面就是进一步的抽象化,filter目前只适用于Apple,我们还可以针对其他类型。
1.7 抽象化List列表
public interface Predicate<T>{
public boolean test(T t);
}
public static List<T> filter(List<T> inventory, Predicate p){
List<T> result = new ArrayList<>();
for(T t: inventory){
if(p.test(t)){
result.add(t);
}
}
return result;
}
filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
filter(inventory, (Integer i) -> i == 1);
2.其他真实应用
2.1 用Comparator进行排序
我们都知道在JAVA8中List自带了sort方法,你也可以自定义sort。sort的行为可以用java.util.Comparator对象来参数化,它的接口如下:
//java.util.Comparator
public interface Comparator<T>{
public int compare(T o1, T o2);
}
你可以随时创建Comparator的实现,进行不同的sort行为。
inventory.sort(new Comparator<Apple>(){
@override
public int compare(Apple p1, Apple p2){
return p1.getWeight().compareTo(p2.getWeight());
}
});
使用Lambda表达式的话,就是
inventory.sort((Apple p1, Apple p2) -> p1.getWeight().compareTo(p2.getWeight()));
2.2 用Runnable执行代码块
在JAVA中我们可以用线城执行代码块。
//java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(new Runnable(){
@override
public void run(){
System.out.println("Hello world!");
}
});
用Lambda表达式的话
Thread t = new Thread(() -> System.out.println("Hello world!"));