經過行爲參數化傳遞代碼

應對不斷變化的需求

在實際的工做中咱們會將現實問題抽象成對象並對其進行處理,好比須要對一堆顏色和重量不一樣的蘋果進行過濾分類。java

一、蘋果實體類算法

public class Apple {
    // 顏色
    private String color;
    // 重量
    private Integer weight;

    // Getter and Setter
}

二、過濾方法設計模式

public static List<Apple> filter(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;
    }

經過定製過濾方法,好比後期蘋果可能會有其餘的屬性,是否成熟、產地等。咱們能夠在過濾方法的入參加上對應的屬性並在內部進行判斷。這就是經過修改過濾方法來 應對不斷變化的需求。但這樣有其侷限性,若是需求不斷地更改,那麼就須要重寫不少類似的方法。這違背了app

DRY(Don't Repeat Yourself)ide

的軟件工程原則。設計

行爲參數化

咱們其實能夠經過標準建模來定義一個過濾接口,讓其比重寫不少次過濾方法更好地 應對不斷變化的需求code

一、創建蘋果謂詞接口對象

// predicate:謂詞,即一個返回 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();
    }
}

策略模式做爲一種軟件設計模式,指對象有某個行爲,可是在不一樣的場景中,該行爲有不一樣的實現算法。ip

咱們能夠將 AppleColorPredicateAppleWeightPredicate 看做是 過濾方法 的不一樣行爲,須要 過濾方法 接收 ApplePredicate 對象對蘋果進行過濾。這就是 行爲參數化:讓方法接收多種行爲(或策略)做爲參數,並在內部使用,來完成不一樣的行爲。

一、修改過濾方法讓其可以接收蘋果謂詞接口對象

public static List<Apple> filter(List<Apple> appleList, ApplePredicate applePredicate) {
        // 符合條件的蘋果集合
        List<Apple> result = new ArrayList<>();
        for (Apple apple : appleList) {
            // 若是符合條件就存入
            if (applePredicate.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }

二、調用過濾方法進行過濾

public class Main {
    public static void main(String[] args) {
        List<Apple> appleList = new ArrayList<>();

        Apple apple = new Apple();
        apple.setColor("red");
        apple.setWeight(1);
        appleList.add(apple);

        apple = new Apple();
        apple.setColor("green");
        apple.setWeight(2);
        appleList.add(apple);

        List<Apple> result = filter(appleList, new AppleWeightPredicate());
    }
}

result 中就會只有重量大於1的蘋果集合了。行爲參數化 的好處在於咱們能夠把過濾的邏輯 boolean test() 與應用過濾的行爲 public static List<Apple> filter() 解耦。這樣在需求不斷更改時,只須要新增 ApplePredicate 實現再調用就行。

對付囉嗦

然而按照以上方式使用 ApplePredicate 依然有一個問題,那就是咱們仍是得不斷地新增 ApplePredicate 的實現。本質上只是把重寫過濾方法的代價轉移到了新增謂詞實現上。這個時候咱們能夠換一個思路出發,使用 匿名類 來隨用隨建謂詞實現。

使用匿名類實現謂詞接口

List<Apple> result = filter(appleList, new ApplePredicate() {
            @Override
            public boolean test(Apple apple) {
                // 選出綠蘋果且重量爲2
                return "green".equalsIgnoreCase(apple.getColor()) && 2 == apple.getWeight();
            }
        });

如今,咱們只須要每次去匿名實現謂詞接口就行,然而這樣的寫讓人以爲很臃腫,並且看起來很讓人費解。接下來看看 Lambda 是怎麼讓其變得簡潔又友好的。

經過 Lambda 簡化匿名實現

List<Apple> result = filter(appleList, (Apple apple1) -> "green".equalsIgnoreCase(apple1.getColor()) && 2 == apple1.getWeight());

是否是簡潔得有點看不懂了?不要緊,先細細品味,下一章咱們會詳細瞭解 Lambda。

咱們還能夠進一步抽象。目前 ApplePredicate 還只適用於蘋果,而我想要其餘對象進行過濾呢?可使用泛型來定義須要處理的對象。

一、修改 ApplePredicate 成 Predicate

public interface Predicate<T> {
    boolean test(T t);
}

二、修改過濾方法

public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        // 符合條件的集合
        List<T> result = new ArrayList<>();
        for (T t : list) {
            // 若是符合條件就存入
            if (predicate.test(t)) {
                result.add(t);
            }
        }
        return result;
    }

這樣咱們就能將過濾方法用在其餘對象上了。下一章咱們會更加深刻地理解 Lambda 是什麼,能幹什麼。

Java 8 實戰 第二章 經過行爲參數化傳遞代碼 讀書筆記

這是我第一篇文章,歡迎加入咖啡館的春天(338147322)。

相關文章
相關標籤/搜索