2 通过行为参数化传递代码
2.1 应对不断变化的需求
2.1.1 实现一个从列表中筛选绿色苹果的功能
public static class Apple {
private String color;
private int weight;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
突出显示的行就是筛选绿苹果所需的条件。但是现在农民改主意了,他还想要筛选红苹果。
public static List<Apple> filterGreenByColor(List<Apple> inventory) {
List<Apple> result = new ArrayList<>(); // 苹果列表
for (Apple apple : inventory) {
if ("green".equals(apple.getColor())) { // 仅仅选出绿色苹果
result.add(apple);
}
}
return result;
}
2.1.2 把颜色作为参数
一种做法是给方法加一个参数,把颜色变成参数,这样就能灵活地适应变化了
public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
List<Apple> result = new ArrayList<Apple>();
for (Apple apple : inventory) {
if (apple.getColor().equals(color)) {
result.add(apple);
}
}
return result;
}
只要像下面这样调用方法,农民朋友就会满意了
List<Apple> greenApples = filterApplesByColor(apples,"green");
List<Apple> redApples = filterApplesByColor(apples,"red");
这位农民又跑回来和你说:要是能区分轻的苹果和重的苹果就太好了。重的苹果一般是重量大于150克
/**
*
* 复制了大部分的代码来实现遍历库存 并对每个苹果应用筛
* 选条件。这有点儿令人失望,因为它打破了DRY( Don’t Repeat Yourself,不要重复自己)的软件
* 工程原则。如果你想要改变筛选遍历方式来提升性能呢?那就得修改所有方法的实现,而不是只
* 改一个。从工程工作量的角度来看,这代价太大了
* @param inventory
* @param weight
* @return
*/
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
List<Apple> result = new ArrayList<Apple>();
for (Apple apple : inventory) {
if (apple.getWeight() > weight) {
result.add(apple);
}
}
return result;
}
2.1.3 对能想到的每个属性做筛选
对你能想到的每个属性做筛选 这个解决方法不能很好的应对变化的需求
public static List<Apple> filterApples(List<Apple> inventory,String color,int weight,boolean flag){
List<Apple> result = new ArrayList<Apple>();
for (Apple apple : inventory) {
if((flag && apple.getColor().equals(color))|| (!flag && apple.getWeight() > weight )){
result.add(apple);
}
}
return result;
}
2.2 行为参数化
/**
* 行为参数化
* 需要一种比添加很多参数更好的方法来应对变化的需求。让
* 我们后退一步来看看更高层次的抽象。一种可能的解决方案是对你的选择标准建模:你考虑的
* 是苹果,需要根据Apple的某些属性(比如它是绿色的吗?重量超过150克吗?)来返回一个
* boolean值。我们把它称为谓词(即一个返回boolean值的函数)。
*/
public interface ApplePredicate{
boolean test(Apple apple);
}
/**
* 不同的策略
*/
public class AppGreenColorPredicate implements ApplePredicate{
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}
public class AppleHeavyWeightPredicate implements ApplePredicate{
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
1 传递代码/行为
这段代码比我们第一次尝试的时候灵活多了读起来 用起来也更容易!可以创建不同的ApplePredicate对象,并将它们传递给filterApples方法。免费的灵活性!比如,如果需要农民找出所有重量超过150克的红苹果,只需要创建一个类来实现ApplePredicate就行了。代码现在足够灵活,可以应对任何涉及苹果属性的需求变更了
public List<Apple> filterApples(List<Apple> inventory,ApplePredicate p){
List<Apple> result = new ArrayList<>();
for (One.Apple apple : inventory) {
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
2 多种行为 一个参数
行为参数化的好处在于你可以把迭代要筛选的集合的逻辑与对集合中每个元素应用的行为区分开来。这样你可以重复使用同一个方法,给它不同的行为来达到不同的目的
2.3 对付啰嗦
新增功能时需要实现 ApplePredicate接口 使用新功能时要NEW ApplePredicate得实现类
List<One.Apple> apples1 = filterApples(appleList,new AppleRedAndHeavyPredicate());
List<One.Apple> apples2 = filterApples(appleList,new AppleHeavyWeightPredicate());
public List<Apple> filterApples(List<Apple> inventory,ApplePredicate p){
List<Apple> result = new ArrayList<>();
for (One.Apple apple : inventory) {
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
2.3.1 匿名类
匿名类往往很笨重,因为它占用了很多空间
filterApples(appleList, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return false;
}
});
2.3.2 使用 Lambda 表达式
简洁
List<Apple> red = filterApples(appleList, (Apple a) -> a.equals("red"));
2.3.3 将List类型抽象化
public interface Predicate<T>{
boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> result = new ArrayList<>();
for(T e: list){
if(p.test(e)){
result.add(e);
}
}
return result;
}
在可以把filter方法用在香蕉、桔子、 Integer或是String的列表上了
2.3.4 用Comparator来排序
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
2.4 小结
行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们, 完成不同行为的能力
行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。
传递代码,就是将新行为作为参数传递给方法。但在Java 8之前这实现起来很啰嗦。为接口声明许多只用一次的实体类而造成的啰嗦代码,在Java 8之前可以用匿名类来减少。