文章目录
1、认识行为参数化
通过相关书籍或网络搜索得到的行为参数化的解释通常是这样定义的:
行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式。一言以蔽之,它意味着拿出一个代码块,把它准备好却不去执行它。这个代码块以后可以被你程序的其他部分调用,这意味着你可以推迟这块代码的执行。
仔细理解之后领会了其表达的含义但是还是感觉有点抽象,因此我通过一个示例来帮助自己理解行为参数化:
我要买一个红色的篮球
我要买一支黑色的手表
其中,【…要买…】就是一种行为,那么商品(篮球、手表)就是参数,通过判断商品的属性来满足我的行为。那么还会存在以下行为:
张三要买一个红色的篮球
李四要买一支黑色的手表
该种情况仍然需要判断商品的属性来满足行为,那么设想一下,如果把【…要买…】这种行为作为一个参数,岂不是该类行为,使用同一个逻辑判断就可以满足了。
接下来,本文会通过一个示例,一步步向行为参数化递进,一起学习一下行为参数化与普通参数传递到底有什么区别。
2、示例推演行为参数化
以下都会通过筛选苹果的示例,贯穿整个推演过程,废话少说,开始啦。。。
2.1、筛选绿苹果
//构造数据
public static List<Apple> appleList = Arrays.asList(new Apple(60, "green"), new Apple(80, "yellow"), new Apple(200, "red"));
public static void main(String[] args) {
System.out.println(filterGreenApples(appleList));
}
/**
* 筛选绿色的苹果
*/
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if ("green".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
我们再代码里写死了绿色苹果的判断条件,如果我们要筛选黄色、红色的苹果时,该方法就无法满足了。
2.2、颜色作为参数筛选
针对以上情况,我们会想到将颜色作为参数传递,这样该方法就满足了筛选其它颜色的需求了。
//构造数据
public static List<Apple> appleList = Arrays.asList(new Apple(60, "green"), new Apple(80, "yellow"), new Apple(200, "red"));
public static void main(String[] args) {
// System.out.println(filterGreenApples(appleList));
System.out.println(filterApplesByColor(appleList,"red"));
}
/**
* 筛选指定颜色的苹果
* @param appleList 苹果集合对象
* @param color 苹果颜色
*/
public static List<Apple> filterApplesByColor(List<Apple> appleList, String color){
List<Apple> result = new ArrayList<>();
for(Apple apple: appleList){
if(apple.getColor().equals(color)){
result.add(apple);
}
}
return result;
}
2.3、多个属性作为参数筛选
如果现在需求变化了,要求按照颜色和重量两个属性筛选苹果,那么我们就把能想到的属性都作为参数进行处理。
//构造数据
public static List<Apple> appleList = Arrays.asList(new Apple(60, "green"), new Apple(80, "yellow"), new Apple(200, "red"));
public static void main(String[] args) {
// System.out.println(filterGreenApples(appleList));
// System.out.println(filterApplesByColor(appleList,"red"));
System.out.println(filterApplesByParam(appleList, "red", 80));
}
/**
* 筛选符合条件的苹果
*
* @param appleList 苹果集合对象
* @param color 苹果颜色
* @param weight 苹果重量
*/
public static List<Apple> filterApplesByParam(List<Apple> appleList, String color, int weight) {
// 符合条件的苹果集合
List<Apple> result = new ArrayList<>();
for (Apple apple : appleList) {
// 如果颜色和重量都符合条件
if (color.equalsIgnoreCase(apple.getColor()) && weight == apple.getWeight()) {
result.add(apple);
}
}
return result;
}
通过定制过滤方法,比如后期苹果可能会有其他的属性,是否成熟、产地等。我们可以在过滤方法的入参加上对应的属性并在内部进行判断。这就是通过修改过滤方法来 应对不断变化的需求。但这样有其局限性,如果需求不断地更改,那么就需要重写很多相似的方法。这违背了 DRY(Don’t Repeat Yourself) 的软件工程原则。
2.4、行为参数化
我们变换一下角度,站在一个更高层次抽象的角度,来解决这个问题。我们考虑的对象时苹果,需要根据苹果的属性(颜色、重量等等)来判断是否符合我们的要求,判断的结果是一个 boolean 值,我们称为 谓词。下面我们就根据这种抽象进行标准建模:
public interface ApplePredicate {
boolean test(Apple apple);
}
下面我们可以运用策略模式思想来构建具体实现:
public class AppleColorPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
// 选出绿色的苹果
return "green".equalsIgnoreCase(apple.getColor());
}
}
public class AppleWeightPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
// 选出重量大于1的苹果
return 1 < apple.getWeight();
}
}
我们可以将 AppleColorPredicate 和 AppleWeightPredicate 看作是过滤方法的不同行为,过滤方法通过接收 ApplePredicate 对象对苹果进行过滤,这就是行为参数化。
行为参数化的好处在于你可以把迭代要筛选的集合的逻辑与对集合中每个元素的应用的行为区分开来。这样你可以重复使用同一个方法,给它不同的行为来达到不同的目的,如下图所示。
2.5、根据抽象条件筛选
根据上文的行为参数化的抽象,目前的实现方法如下:
//构造数据
public static List<Apple> appleList = Arrays.asList(new Apple(60, "green"), new Apple(80, "yellow"), new Apple(200, "red"));
public static void main(String[] args) {
// System.out.println(filterGreenApples(appleList));
// System.out.println(filterApplesByColor(appleList,"red"));
// System.out.println(filterApplesByParam(appleList, "red", 80));
System.out.println(filterApplesByParam(appleList, new AppleWeightPredicate()));
}
/**
* 筛选苹果
*
* @param appleList 苹果集合对象
* @param applePredicate 行为参数化,苹果抽象谓词判断对象
*
*/
public static List<Apple> filterApplesByParam(List<Apple> appleList, ApplePredicate applePredicate) {
// 符合条件的苹果集合
List<Apple> result = new ArrayList<>();
for (Apple apple : appleList) {
if (applePredicate.test(apple)) {
result.add(apple);
}
}
return result;
}
2.6、使用匿名类
当把新的行为传入filterApples方法的时候,你不得不声明好几个实现ApplePredicate的接口的实现类,然后实例化好几个只会用到一次的ApplePredicate对象,显得有点啰嗦并且很浪费时间。
下面我们将展示,如何使用一个匿名类实现 ApplePredicate 对象,重写筛选的实现。
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple a){
return a.getColor().equals("red");
}
});
直接内联参数化过滤苹果的方法作为参数。但是匿名类还是不够好,它显得很笨重,每次都要带着具体的实现逻辑,显得很臃肿。
2.7、使用Lambda表达式
针对匿名类的不好,我们可以通过使用lambda表达式来实现,如下:
List<Apple> result = filter(appleList, (Apple apple) -> "red".equalsIgnoreCase(apple1.getColor()));
2.8、List 类型抽象化
目前的过滤方法只使用于Apple,我们可以将 List 类型抽象化,来使用更多的对象。
interface Predicate<T>{
public 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;
}
public static void main(String[] args) {
// System.out.println(filterGreenApples(appleList));
// System.out.println(filterApplesByColor(appleList,"red"));
// System.out.println(filterApplesByParam(appleList, "red", 80));
// System.out.println(filterApplesByParam(appleList, new AppleWeightPredicate()));
filter(appleList,(Apple apple)->"red".equals(apple.getColor()));
filter(Arrays.asList(10,55,12,41),i->i%2==0);
}
3、行为参数化在 Java API中的应用实例
现在我们已经看到,行为参数化是一个很有用的模式,它可以轻松应对不断变化的需求。这种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创建的行为将方法的行为参数化,这种做法类似于策略设计模式
3.1、用 Comparator 排序
在java8中,List自带了一个sort的方法(你可以直接使用Collections.sort)。sort的行为可以用java.util.Comparator对象来参数化,它的接口如下:
public interface Comparator<T> {
int compare(T o1, T o2);
}
按照苹果重量升序排序
1、匿名类方式
inventory.sort(new Comparator<Apple>(){
public int compare(Apple apple1, Apple apple2){
return apple1.getWeight().compareTo(apple2.getWeight());
}
})
2、Lambda表达式
appleList.sort((Apple apple1,Apple apple2)->apple1.getWeight().compareTo(apple2.getWeight()));
或
appleList.sort(Comparator.comparing(Apple::getWeight));
3.2、用 Runnable 执行代码块
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实战》(中文版)