Java8特性詳解(一):行爲參數化--將代碼傳遞給方法

什麼是行爲參數化

行爲參數化是使方法接受多種行爲作爲參數,並在內部使用,完成不同的行爲。行爲參數化可讓代碼更好地適應不斷變化的要求,減輕未來的工作量,是可以幫助你處理頻繁變更的需求的一種軟件開發模式。

需要指出的是,行爲參數化不是Java8的新特性,但是是Java8新特性的重要基礎和思想。

舉例解釋:假設有這樣一個場景,小明開車去超市買東西,我們可以定義一個goAndBuy()方法。但針對另一個場景,小明開車去醫院掛號或者去動物園看猩猩,使用goAndBuy方法顯然是不正確的。如果我們定義go方法,將買東西、掛號、看猩猩等不同行爲作爲參數,送給go方法執行,則能夠很好地適應新場景。

理解行爲參數化的經典案例–蘋果的故事

案例中不斷變換需求,使得不得不重構代碼應對新的變化。

1 定義蘋果對象類

public static class Apple {

    private int weight; // 重量
    private String color; // 顏色

    public Apple(int weight, String color) {
        this.weight = weight;
        this.color = color;
    }

    public Integer getWeight() {
        return weight;
    }

    public void setWeight(Integer weight) {
        this.weight = weight;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Apple{" + "weight=" + weight + ", color='" + color + '\'' + '}';
    }
}

2 根據顏色篩選蘋果(值參數化)

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

List<Apple> greenApples = filterGreenApples(inventory);

針對於蘋果存貨列表inventory,我們做遍歷操作,輕而易舉地獲取到綠色蘋果列表。此時,修改需求轉爲選出綠色蘋果,我們會修改上述方法:

public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getColor().equals(color)) {
            result.add(apple);
        }
    }
    return result;
}

List<Apple> redApples = filterApplesByColor(inventory, "red");

通過添加color形參,解決了不同顏色水果的問題。然而Apple類有多個屬性,此時需求改成按照蘋果重量篩選,以及按照蘋果顏色和重量篩選,上面的方法便不能很好的應對,也容易造成代碼的冗餘。

public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getWeight() > weight) {
            result.add(apple);
        }
    }
    return result;
}

List<Apple> weightBiggerThan120Apples = filterApplesByWeight(inventory, 120);

3 使用匿名內部類進行優化

顯而易見,方法代碼中存在大量重複代碼,唯一不同的便是篩選條件不同。我們將篩選這一行爲,從filterApples方法中單獨取出。

定義篩選條件接口

public interface ApplePredicate {
    boolean test(Apple apple);
}

同時繼續修改過濾函數

public static List<Apple> filterApples2(List<Apple> inventory, ApplePredicate predicate) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (predicate.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

方法調用–採用的策略是綠色蘋果,重量大於150g。

List<Apple> heavyApplesByAnonyInnerClass = filterApples2(inventory, new ApplePredicate() {
    @Override
    public boolean test(Apple apple) {
        return "green".equals(apple.color) && apple.getWeight() > 150;
    }
});

定義ApplePredicate接口,作爲對每個Apple元素判斷行爲的策略規範。給filterApples方法添加一個參數,讓它接受ApplePredicate對象,這就相當於將對Apple判斷行爲test傳遞給了filterApples方法。此時,我們可以任意修改策略,來滿足不同場景的需求,而不需要變更filterApples方法。

這一過程即是行爲參數化:讓方法接受多種行爲(策略)做參數,並表現出不同目的。行爲參數化,很好的將迭代整個Apple列表的操作與針對每個Apple元素的操作做了解耦,從而能夠重複使用同一個方法,給予不同行爲參數,獲取不同目的。

行爲參數化與策略模式息息相關,定義一系列算法,將它們封裝起來,並且使他們可相互替換。本小節使用的是匿名內部類,如果傳入的是實現類,可以更加直觀的理解策略模式的使用。

4 使用Lambda表達式繼續優化

在Java8之前,方法只能接受對象。因此對Apple元素的判斷行爲代碼必須要包裹在對象(ApplePredicate對象)中傳遞給filterApples方法。同時匿名內部類雖然優於具體實現類,但仍有一些不必要代碼,並且易讀性差。

Java8開始,我們有了更好的解決方案–使用Lambda表達式,清晰易讀,代碼量少。

List<Apple> heavyApplesLambda = filterApples2(inventory, 
	apple -> "green".equals(apple.color) && apple.getWeight() > 150);

5 小結

在這裏插入圖片描述
值參數化中的值,包含基本類型、對象(對象引用)。Java8之前我們只能將基本類型和對象引用傳遞給方法。上方的類和匿名類代碼實現上是值參數化(傳遞類給方法),將其歸於行爲參數化,是因爲他們本身只是爲了傳遞行爲。Java8之後,
可以將方法引用和lambda代碼傳遞給方法,更加體現行爲參數化這一軟件開發模式–將代碼傳遞給方法。

參考資料

  1. Java8實戰
  2. 設計模式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章