挑苹果中的行为参数化思想

草捏对Lambda表达式的了解停留在用IDEA自动替换匿名类的程度,觉得Lambda表达式只是把代码缩短了而已,不过就是一个语法糖。因此一直不屑一顾,没系统学习。“不过就是代码短一点嘛,没啥大不了”。但经过学习才知道Lambda表达式不单单是把代码换了种表达方式,或许更重要的是背后的思想——行为参数化。编程

所谓的行为参数化,指的是咱们能够经过参数传递的形式去指定代码的行为。是否是很眼熟,学过设计模式的童鞋,基本都是从策略模式开始学起的。策略模式是指面向接口编程,经过使用不一样的实现类,改变具体的行为。行为参数化和策略模式的效果相似,只是多了个参数化,经过传递参数来指定行为。设计模式

下面草捏给你们讲个关于挑苹果的小故事。app

梅梅开始计划天天吃一个苹果,因而吩咐草捏去超市采购。草捏(一个没有生活经验的男人)买苹果的原则:不是烂的就行。ide

// 不是烂的就行
// isRotten = false
public static List<Apple> filterApple(List<Apple> apples, boolean isRotten) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : apples) {
        if (apple.getRotten().equals(isRotten)) {
            result.add(apple);
        }
    }
    return result;
}

兴致勃勃的买完回到家。
:“草捏,这苹果里咋还有个绿的,我喜欢吃红苹果,要买红的!!!”
:“啊,知道了,知道了, 买红的,下次买红的。”函数

// 此次知道了不只不是烂的,还要买红的
// isRotten = false, color = "red"
public static List<Apple> filterApple(List<Apple> apples, boolean isRotten, boolean color) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : apples) {
        if (apple.getRotten().equals(isRotten) &&
            apple.getColor().equals(color)) {
            result.add(apple);
        }
    }
    return result;
}

第二次买苹果归来,等待检验。
:“嗯,此次是清一色的红苹果了,但是我喜欢吃小点的苹果,更可爱些,直径应该小于5厘米。”
:“直径...小于...5厘米...”
:“有问题吗?”
:“好的,好的,下次买小于5厘米的。”学习

// 又要加一个参数,直径小于5厘米
// isRotten = false, color = "red", diameter = 5
public static List<Apple> filterApple(List<Apple> apples, boolean isRotten, boolean color, double diameter) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : apples) {
        if (apple.getRotten().equals(isRotten) &&
                apple.getColor().equals(color) &&
                (apple.getDiameter() < diameter)) {
            result.add(apple);
        }
    }
    return result;
}

草捏发现,函数的参数已经有4个了,已经不少了,是否是能够考虑改写一下,传递的参数都是挑选苹果的相关标准,而后在函数中根据这些参数来筛选,是否是能够把这些参数抽象成一个结构体,这里抽象成一个Apple类型的变量。设计

public static List<Apple> filterApple(List<Apple> apples, Apple standard) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : apples) {
        if (apple.getRotten().equals(standard.isRotten) &&
                apple.getColor().equals(standard.color) &&
                (apple.getDiameter() < standard.diameter)) {
            result.add(apple);
        }
    }
    return result;
}

草捏想这下应该完美了吧,直到第三次回来。
:“草捏,我又想吃大苹果了,下次你买大苹果回来吧。直径大于5厘米的那种。”
这需求变的可真快啊。要大苹果,那就是修改<为>,简单!code

public static List<Apple> filterApple(List<Apple> apples, Apple standard) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : apples) {
        if (apple.getRotten().equals(standard.isRotten) &&
                apple.getColor().equals(standard.color) &&
                // 筛选大苹果
                (apple.getDiameter() > standard.diameter)) {
            result.add(apple);
        }
    }
    return result;
}

可是看着这两个版本的代码,草捏察觉filterAppple中每次变动的是判断苹果是否符合标准的代码,至于遍历apples和根据判断结果加入到result中这部分是不变化的。因此若是能把判断标准的代码抽象出来,那每次修改的影响就会更小(不用改动filterApple方法)。
定义一个判断苹果标准的接口:接口

// 苹果标准判断
public interface AppleStandardPredicate {
    // 是否符合标准
    boolean isMeetStandard(Apple apple);
}

再来个具体实现类,大苹果标准判断:ip

public class BigAppleStandardPredicate implements AppleStandardPredicate {
    @Override
    public boolean isMeetStandard(Apple apple) {
        if (apple.getRotten().equals(false) &&
                apple.getColor().equals("red") &&
                (apple.getDiameter() > 5)) {
            return true;
        }
        return false;
    }
}

把filterApple 的参数改成AppleStandardPredicate,这样filterApple将不会因苹果判断逻辑的变化而变化了。

public static List<Apple> filterApple(List<Apple> apples, AppleStandardPredicate predicate) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : apples) {
        if (predicate.isMeetStandard(apple)) {
            result.add(apple);
        }
    }
    return result;
}

让咱们为filterApple传入BigAppleStandardPredicate:

List<Apple> goodApples = filterApple(apples, new BigAppleStandardPredicate());

但这种写法比较繁琐的地方在于须要建立一个实现类,若是用匿名内部类可能会更简洁些。

List<Apple> goodApples = filterApple(apples, new AppleStandardPredicate() {
    @Override
    public boolean isMeetStandard(Apple apple) {
        if (apple.getRotten().equals(false) &&
                apple.getColor().equals("red") &&
                (apple.getDiameter() > 5)) {
            return true;
        }
        return false;
    }
});

嗯,类是少建立了一个,但好像也不是那么简洁,试着用Lambda再稍微简化下。

List<Apple> goodApples = filterApple(apples, apple -> {
    if (apple.getRotten().equals(false) &&
            apple.getColor().equals("red") &&
            (apple.getDiameter() > 5)) {
        return true;
    }
    return false;
});

:“草捏,我不想吃苹果了,我想吃蛇果!标准和以前苹果的同样。”
:“好的。”
这下该怎么改呢?行为仍是原来的行为,可是类型换了。那就用泛型吧。
把AppleStandardPredicate改成带泛型的StandardPredicate:

public interface StandardPredicate<T> {
    boolean isMeetStandard(T object);
}

把filterApple改成带泛型的filter:

public static <T> List<T> filter(List<T> objects, StandardPredicate<T> predicate) {
    List<T> result = new ArrayList<>();
    for (T object : objects) {
        if (predicate.isMeetStandard(object)) {
            result.add(object);
        }
    }
    return result;
}

最后在调用filter时,只是修改了下变量名,其余都没改,仍然是原来的逻辑。

List<SnakeApple> goodSnakeApples = filter(snakeApples, snakeApple -> {
    if (snakeApple.getRotten().equals(false) &&
            snakeApple.getColor().equals("red") &&
            (snakeApple.getDiameter() > 5)) {
        return true;
    }
    return false;
});

经过类型抽象化,让StandardPredicate和Filter的适用范围扩大化了,不只能够用这段代码挑苹果和蛇果,你还能拿着这段代码去买菜!
:”草捏,去买点卷心菜回来~“
:“好的~”

List<Cabbage> goodCabbages = filter(cabbages, cabbage -> cabbage.getColor().equals("green"));

甚至还能帮老婆挑化妆品!
:”草捏,520快到了,口红用完了~“
:“好的~”

List<Lipstick> goodLipsticks = filter(lipsticks, lipstick -> lipstick.getPrice() > 500);

真是妙啊~ 实乃居家生活之必备~

相关文章
相关标签/搜索