挑蘋果中的行爲參數化思想

草捏對Lambda表達式的瞭解停留在用IDEA自動替換匿名類的程度,覺得Lambda表達式只是把代碼縮短了而已,不過就是一個語法糖。因此一直不屑一顧,沒系統學習。「不過就是代碼短一點嘛,沒啥大不了」。但經過學習才知道Lambda表達式不單單是把代碼換了種表達方式,可能更重要的是背後的思想——行爲參數化java

所謂的行爲參數化,指的是咱們能夠經過參數傳遞的形式去指定代碼的行爲。是否是很眼熟,學過設計模式的童鞋,基本都是從策略模式開始學起的。策略模式即是面向接口編程,經過使用不一樣的實現類,改變具體的行爲。行爲參數化和策略模式的效果相似,只是多了個參數化,經過傳遞參數指定了具體的形式。編程

下面草捏給你們講個關於挑蘋果的小故事。設計模式

梅梅開始計劃天天吃一個蘋果,因而吩咐草捏去超市採購。草捏(一個沒有生活經驗的男人)買蘋果的原則就是否是爛的就行。app

// 不是爛的就行
// 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;
}

興致勃勃的買完回到家。ide

😠:「草捏,這蘋果裏咋還有個綠的🍎🍎🍎🍏,我喜歡吃紅蘋果,要買紅的!!!」函數

😅:「啊,知道了,知道了, 買紅的,下次買紅的。」學習

// 此次知道了不只不是爛的,還要買紅的
// 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;
}

第二次買蘋果歸來,等待檢驗。spa

🙂:「嗯,此次是清一色的紅蘋果了🍎🍎🍎🍎,但是我喜歡吃小點的蘋果,更可愛些,直徑應該小於5釐米。」設計

😨:「直徑...小於...5釐米...」code

😠:「有問題嗎?」

😅:「好的,好的,下次買小於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釐米的那種。」

這需求變的可真快啊。要大蘋果,那就是修改<>,簡單!

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

再來個具體實現類,大蘋果標準判斷:

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

經過類型抽象化,讓StandardPredicateFilter的適用範圍擴大化了,不只能夠用這段代碼挑蘋果和蛇果,你還能拿着這段代碼去買菜!

🥰:」草捏,去買點捲心菜回來~「

😁:「好的」

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

甚至還能幫老婆挑化妝品!

🥰:」草捏,520快到了,口紅用完了「

😁:「好的」

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

真是妙啊~ 實乃居家生活之必備~

相關文章
相關標籤/搜索