Java8实战-总结5

Java8实战-总结5

  • 通过行为参数化传递代码
    • 行为参数化
      • 第四次尝试:根据抽象条件筛选
        • 传递代码/行为
        • 多种行为,一个参数

通过行为参数化传递代码

行为参数化

在筛选苹果的例子中,需要一种比添加很多参数更好的方法来应对变化的需求。后退一步来看看更高层次的抽象。一种可能的解决方案是对选择标准建模:你考虑的是苹果,需要根据Apple的某些属性(比如它是绿色的吗?重量超过150克吗?)来返回一个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());
	}
}

选择苹果的不同策略:
Java8实战-总结5_第1张图片
可以把这些标准看作filter方法的不同行为。刚刚做的这些和“策略设计模式”相关,它让你定义一族算法,把它们封装起来(称为“策略”),然后在运行时选择一个算法。在这里,算法族就是ApplePredicate,不同的策略就是AppleHeavyWeightPredicateAppleGreenColorPredicate

但是,该怎么利用ApplePredicate的不同实现呢?需要filterApples方法接受ApplePredicate对象,对Apple做条件测试。这就是行为参数化:让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为。

要在例子中实现这一点,要给filterApples方法添加一个参数,让它接受ApplePredicate对象。这在软件工程上有很大好处:现在把filterApples方法迭代集合的逻辑与要应用到集合中每个元素的行为(这里是一个谓词)区分开了。

第四次尝试:根据抽象条件筛选

利用ApplePredicate改过之后,filter方法看起来是这样的:

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;
}

传递代码/行为

这段代码比第一次尝试的时候灵活多了,读起来、用起来也更容易!现在可以创建不同的ApplePredicate对象,并将它们传递给filterApples方法。免费的灵活性!比如,如果农民让你找出所有重量超过150克的红苹果,你只需要创建一个类来实现ApplePredicate就行了。你的代码现在足够灵活,可以应对任何涉及苹果属性的需求变更了:

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方法的行为参数化了!

请注意,在上一个例子中,唯一重要的代码是test方法的实现,如下图所示;正是它定义了filterApples方法的新行为。但令人遗憾的是,由于该filterApples方法只能接受对象,所以必须把代码包裹在ApplePredicate对象里。这种做法就类似于在内联“传递代码”,因为是通过一个实现了test方法的对象来传递布尔表达式的。通过使用Lambda,可以直接把表达式 "red".equals(apple.getcolor()) && apple.getWeight() > 150传递给filterApples方法,而无需定义多个ApplePredicate类,从而去掉不必要的代码。
Java8实战-总结5_第2张图片

多种行为,一个参数

正如我们先前解释的那样,行为参数化的好处在于你可以把迭代要筛选的集合的逻辑与对集合中每个元素应用的行为区分开来。这样你可以重复使用同一个方法,给它不同的行为来达到不同的目的,如x下图所示:
Java8实战-总结5_第3张图片
这就是说行为参数化是一个有用的概念的原因。应该把它放进工具箱里,用来编写灵活的API

测验:编写灵活的prettyPrintApple方法
编写一个prettyPrintApple方法,它接受一个AppleList,并可以对它参数化,以多种方式根据苹果生成一个String输出(有点儿像多个可定制的toString方法)。例如,可以告诉prettyPrintApple方法,只打印每个苹果的重量。此外,可以让prettyPrintApple方法分别打印每个苹果,然后说明它是重的还是轻的。解决方案和前面讨论的筛选的例子类似。

	public static void prettyPrintApple(List<Apple> inventory, ???) {
		for(Apple apple: inventory) {
			String output = ???.???(apple);
			System.out.println(output);
		}
	}

答案如下:
首先,需要一种表示接受Apple并返回一个格式String值的方法。前面在编写ApplePredicate接口的时候,写过类似的东西:

	public interface AppleFormatter {
		String accept(Apple a);
	}

现在就可以通过实现AppleFormatter方法,来表示多种格式行为了:

	public class AppleFancyFormatter implements AppleFormatter {
		public String accept (Apple apple) {
			String characteristic = apple.getWeight() > 150 ?"heavy" : "light";
			return "A" + characteristic + "" + apple.getColor() + "apple";
		}
	}
	
	public class AppleSimpleFormatter implements AppleFormatter {
		public String accept(Apple apple) {
			return "An apple of" + apple.getWeight() + "g";
		}
	}

最后,需要告诉prettyPrintApple方法接受AppleFormatter对象,并在内部使用它们。可以给prettyPrintApple加上一个参数:

	public static void prettyPrintApple(List<Apple> inventory, AppleFormatter formatter) {
		for(Apple apple: inventory) {
			String output = formatter.accept(apple);
			System.out.println(output);
		}
	}

搞定啦!现在就可以给prettyPrintApple方法传递多种行为了。为此,首先要实例化AppleFormatter的实现,然后把它们作为参数传给prettyPrintApple:

	prettyPrintApple(inventory, new AppleFancyFormatter());

这将产生一个类似于下面的输出:

A light green apple
A heavy red apple

或者试试这个:

prettyPrintApple(inventory, new AppleSimpleFormatter());

这将产生一个类似于下面的输出:

An apple of 80g
An apple of 155g

把行为抽象出来,让代码适应需求的变化,但这个过程很啰嗦,因为需要声明很多只要实例化一次的类。让我们来看看可以怎样改进。

你可能感兴趣的:(java,开发语言)