1. 程式人生 > >公子奇帶你一步一步瞭解Java8中行為引數化

公子奇帶你一步一步瞭解Java8中行為引數化

說明:因為本公子一直從事監獄軟體開發,所以本系列部落格的引入也以此為背景。問題做了簡化,只是為了來講解技術點。

一、問題提出

    今日在好好的擼著程式碼,超哥(民警)找來了,讓把監獄30歲以上的民警找給他。

二、功能實現

    這個簡單。什麼也不用說,程式碼擼起來。首先定義實體類

package com.hz.pojo;

/**
 * 民警實體類
 */
public class Police {
    /**
     * 民警警號
     */
    private String policeNo;
    /**
     * 民警姓名
     */
    private String policeName;
    /**
     * 民警年齡
     */
    private Integer policeAge;
    /**
     * 民警籍貫
     */
    private String policeNativePlace;

    public Police(String policeNo, String policeName, Integer policeAge, String policeNativePlace) {
        this.policeNo = policeNo;
        this.policeName = policeName;
        this.policeAge = policeAge;
        this.policeNativePlace = policeNativePlace;
    }

    public String getPoliceNo() {
        return policeNo;
    }

    public void setPoliceNo(String policeNo) {
        this.policeNo = policeNo;
    }

    public String getPoliceName() {
        return policeName;
    }

    public void setPoliceName(String policeName) {
        this.policeName = policeName;
    }

    public Integer getPoliceAge() {
        return policeAge;
    }

    public void setPoliceAge(Integer policeAge) {
        this.policeAge = policeAge;
    }

    public String getPoliceNativePlace() {
        return policeNativePlace;
    }

    public void setPoliceNativePlace(String policeNativePlace) {
        this.policeNativePlace = policeNativePlace;
    }

    @Override
    public String toString() {
        return "Police{" +
                "policeNo='" + policeNo + '\'' +
                ", policeName='" + policeName + '\'' +
                ", policeAge=" + policeAge +
                ", policeNativePlace='" + policeNativePlace + '\'' +
                '}';
    }
}

然後實現

 1 package com.hz;
 2 
 3 import com.hz.pojo.Police;
 4 
 5 import java.util.ArrayList;
 6 import java.util.Arrays;
 7 import java.util.List;
 8 
 9 public class PoliceMain {
10     public static void main(String[] args) {
11         List<Police> polices = Arrays.asList(new Police("P001", "餘警官", 27, "浙江"),
12                 new Police("P001", "李警官", 32, "安徽"),
13                 new Police("P001", "程警官", 25, "安徽"),
14                 new Police("P001", "楊警官", 35, "浙江"));
15 
16         List<Police> result = new ArrayList<>();
17         for (Police police : polices) {
18             if (police.getPoliceAge() > 30) {
19                 result.add(police);
20             }
21         }
22 
23         System.out.println("查詢結果:" + result);
24     }
25 }

因為30是個隨時會變化的值,我在這裡還很明智的將其作為一個引數並提取為一個方法

package com.hz;

import com.hz.pojo.Police;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PoliceMain {
    public static void main(String[] args) {
        List<Police> polices = Arrays.asList(new Police("P001", "餘警官", 27, "浙江"),
                new Police("P001", "李警官", 32, "安徽"),
                new Police("P001", "程警官", 25, "安徽"),
                new Police("P001", "楊警官", 35, "浙江"));

        List<Police> result = filterPoliceAge(polices, 30);

        System.out.println("查詢結果:" + result);
    }

    /**
     * 民警過濾器
     * @auth 公子奇
     * @date 2019-01-02
     * @param policeContainer 全部民警
     * @param age 年齡
     * @return 符合結果的民警列表
     */
    static List<Police> filterPoliceAge(List<Police> policeContainer, Integer age) {
        List<Police> result = new ArrayList<>();
        for (Police police : policeContainer) {
            if (police.getPoliceAge() > 30) {
                result.add(police);
            }
        }
        return result;
    }
}

寫完後我還沾沾自喜,認為很好的程式碼,隨你年齡怎麼變,我都可以。看來我太天真了,很快問題就來了。

三、問題的進一步引入

    沒過多久,超哥又來了,問能不能把所有籍貫為浙江的民警給找出來。我一看,很容易啊,等我幾分鐘,馬上就好,程式碼繼續擼起來。

 1 package com.hz;
 2 
 3 import com.hz.pojo.Police;
 4 
 5 import java.util.ArrayList;
 6 import java.util.Arrays;
 7 import java.util.List;
 8 
 9 public class PoliceMain {
10     public static void main(String[] args) {
11         List<Police> polices = Arrays.asList(new Police("P001", "餘警官", 27, "浙江"),
12                 new Police("P001", "李警官", 32, "安徽"),
13                 new Police("P001", "程警官", 25, "安徽"),
14                 new Police("P001", "楊警官", 35, "浙江"));
15 
16         List<Police> result = filterPoliceAge(polices, 30);
17         System.out.println("查詢結果1: " + result);
18 
19         System.out.println("-------------");
20 
21         result = filterPoliceNativePlace(polices, "浙江");
22         System.out.println("查詢結果2: " + result);
23     }
24 
25     /**
26      * 民警年齡過濾器
27      * @auth 公子奇
28      * @date 2019-01-02
29      * @param policeContainer 全部民警
30      * @param age 年齡
31      * @return 符合結果的民警列表
32      */
33     static List<Police> filterPoliceAge(List<Police> policeContainer, Integer age) {
34         List<Police> result = new ArrayList<>();
35         for (Police police : policeContainer) {
36             if (police.getPoliceAge() > 30) {
37                 result.add(police);
38             }
39         }
40         return result;
41     }
42 
43     /**
44      * 民警籍貫過濾器
45      * @auth 公子奇
46      * @date 2019-01-02
47      * @param policeContainer 全部民警
48      * @param nativePlace 籍貫
49      * @return 符合結果的民警列表
50      */
51     static List<Police> filterPoliceNativePlace(List<Police> policeContainer, String nativePlace) {
52         List<Police> result = new ArrayList<>();
53         for (Police police : policeContainer) {
54             if (nativePlace.equals(police.getPoliceNativePlace())) {
55                 result.add(police);
56             }
57         }
58         return result;
59     }
60 }

寫完之後,我發現有點不太對勁啊,filterPoliceAge和filterPoliceNativePlace這兩個方法存在大量重複的程式碼,這個很明顯違背了DRY(Don't Repeat Yourself,不要重複自己)的軟體工程原則。隨著後面民警屬性的越來越多,這不是要命嘛!

四、策略設計模式的引入

    分析上述任務,程式碼重複/業務邏輯也差不多,既然如此,策略模式就是很好的解決方案啊!說改就改,程式碼繼續。關於策略模式檢視我的GitHub策略模式介紹。

首先定義一個過濾處理介面

 1 package com.hz.filter;
 2 
 3 import com.hz.pojo.Police;
 4 
 5 /**
 6  * 民警過濾條件
 7  */
 8 public interface PolicePredicate {
 9     boolean test(Police police);
10 }

然後針對這個介面,分別實現年齡和籍貫實現類

package com.hz.filter;

import com.hz.pojo.Police;

/**
 * 民警年齡篩選
 */
public class PoliceAgePredicate implements PolicePredicate {
    @Override
    public boolean test(Police police) {
        return police.getPoliceAge() > 30;
    }
}


package com.hz.filter;

import com.hz.pojo.Police;

/**
 * 民警籍貫篩選
 */
public class PoliceNativePlacePredicate implements PolicePredicate {
    @Override
    public boolean test(Police police) {
        return "浙江".equals(police.getPoliceNativePlace());
    }
}

呼叫

 1 package com.hz;
 2 
 3 import com.hz.filter.PoliceAgePredicate;
 4 import com.hz.filter.PoliceNativePlacePredicate;
 5 import com.hz.filter.PolicePredicate;
 6 import com.hz.pojo.Police;
 7 
 8 import java.util.ArrayList;
 9 import java.util.Arrays;
10 import java.util.List;
11 
12 public class PoliceMain2 {
13     public static void main(String[] args) {
14         List<Police> polices = Arrays.asList(new Police("P001", "餘警官", 27, "浙江"),
15                 new Police("P001", "李警官", 32, "安徽"),
16                 new Police("P001", "程警官", 25, "安徽"),
17                 new Police("P001", "楊警官", 35, "浙江"));
18 
19         List<Police> result = filterPolice(polices, new PoliceAgePredicate());
20         System.out.println("結果1: " + result);
21 
22         System.out.println("--------------");
23 
24         result = filterPolice(polices, new PoliceNativePlacePredicate());
25         System.out.println("結果2: " + result);
26     }
27 
28     /**
29      * 民警篩選器
30      * @param policeList
31      * @param predicate
32      * @return
33      */
34     static List<Police> filterPolice(List<Police> policeList, PolicePredicate predicate) {
35         List<Police> result = new ArrayList<>();
36 
37         for (Police police : policeList) {
38             if (predicate.test(police)) {
39                 result.add(police);
40             }
41         }
42 
43         return result;
44     }
45 }

    到了這裡我感覺已經很完美了,引入了設計模式,靈活性大大的增加了,以後不管他怎麼變化,我這邊只要新增一個實現類就可以了。到此大功告成,走,喝杯咖啡去。

五、設計模式後的進一步思考,匿名類的對比

    咖啡喝完以後,把剛才的程式碼拿出來又欣賞了一篇,感覺真好。不對!後面篩選條件越來越多,我的實現類也會變的非常多,而且這些實現類都執行一步操作,這個實現類有必要去建立嗎?如果不去建立這些實現類,我該怎麼實現功能呢?我突然想到了匿名類。那就實現看看唄!

 1 package com.hz;
 2 
 3 import com.hz.filter.PoliceAgePredicate;
 4 import com.hz.filter.PoliceNativePlacePredicate;
 5 import com.hz.filter.PolicePredicate;
 6 import com.hz.pojo.Police;
 7 
 8 import java.util.ArrayList;
 9 import java.util.Arrays;
10 import java.util.List;
11 
12 public class PoliceMain2 {
13     public static void main(String[] args) {
14         List<Police> polices = Arrays.asList(new Police("P001", "餘警官", 27, "浙江"),
15                 new Police("P001", "李警官", 32, "安徽"),
16                 new Police("P001", "程警官", 25, "安徽"),
17                 new Police("P001", "楊警官", 35, "浙江"));
18 
19         List<Police> result = filterPolice(polices, new PoliceAgePredicate());
20         System.out.println("結果1: " + result);
21 
22         System.out.println("--------------");
23 
24         result = filterPolice(polices, new PoliceNativePlacePredicate());
25         System.out.println("結果2: " + result);
26 
27         System.out.println("----------------");
28 
29         result = filterPolice(polices, new PolicePredicate() {
30             @Override
31             public boolean test(Police police) {
32                 return police.getPoliceAge() > 30;
33             }
34         });
35         System.out.println("結果3: " + result);
36     }
37 
38     /**
39      * 民警篩選器
40      * @param policeList
41      * @param predicate
42      * @return
43      */
44     static List<Police> filterPolice(List<Police> policeList, PolicePredicate predicate) {
45         List<Police> result = new ArrayList<>();
46 
47         for (Police police : policeList) {
48             if (predicate.test(police)) {
49                 result.add(police);
50             }
51         }
52 
53         return result;
54     }
55 }

    這樣即實現了功能,也不需要建立大量的實現類,類增加的同時,也會增加我們的維護成本。後來仔細想了想,也不太好,類的維護是降低了,可是大量的匿名實現從程式碼可讀性上也是存在很大缺陷的,還有更好的方案嗎?

六、Lambda表示式的引入

    以上匿名類,完全是可以通過Java8來進行簡化的。話不多說,程式碼奉上。

package com.hz;

import com.hz.filter.PolicePredicate;
import com.hz.pojo.Police;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PoliceMain3 {
    public static void main(String[] args) {
        List<Police> polices = Arrays.asList(new Police("P001", "餘警官", 27, "浙江"),
                new Police("P002", "李警官", 32, "安徽"),
                new Police("P003", "程警官", 25, "安徽"),
                new Police("P004", "楊警官", 35, "浙江"));

        List<Police> result = filterPolice(polices, (Police police) -> police.getPoliceAge() > 30);
        System.out.println("結果1: " + result);

        System.out.println("---------------");

        result = filterPolice(polices, (Police police) -> "浙江".equals(police.getPoliceNativePlace()));
        System.out.println("結果2: " + result);
    }

    /**
     * 民警篩選器
     * @param policeList
     * @param predicate
     * @return
     */
    static List<Police> filterPolice(List<Police> policeList, PolicePredicate predicate) {
        List<Police> result = new ArrayList<>();

        for (Police police : policeList) {
            if (predicate.test(police)) {
                result.add(police);
            }
        }

        return result;
    }
}

這麼一改,程式碼簡潔了很多,也更加容易理解是什麼意思了。

七、繼續對型別進行抽象化

    剛對民警篩選修改完,感覺不需要再改了,此時超哥帶著“美麗的”笑容向我走來了。需要對監獄罪犯也做同樣的篩選。我的天啊,難道要我把上面的程式碼再針對罪犯複製一遍嗎?作為一名愛學習、求進步的新時代程式設計師,這怎麼能難到我。

    既然如此,看來要從介面上進行修改了,將介面修改為:

package com.hz.filter;

/**
 * 篩選器
 * @param <T>
 */
public interface Predicate<T> {
    boolean test(T t);
}

這樣一改,你需要篩選型別,自己去傳就可以了。

 1 package com.hz;
 2 
 3 import com.hz.filter.Predicate;
 4 import com.hz.pojo.Police;
 5 
 6 import java.util.ArrayList;
 7 import java.util.Arrays;
 8 import java.util.List;
 9 
10 public class PoliceMain4 {
11     public static void main(String[] args) {
12         List<Police> polices = Arrays.asList(new Police("P001", "餘警官", 27, "浙江"),
13                 new Police("P002", "李警官", 32, "安徽"),
14                 new Police("P003", "程警官", 25, "安徽"),
15                 new Police("P004", "楊警官", 35, "浙江"));
16 
17         List<Police> result = filter(polices, (Police police) -> police.getPoliceAge() > 30);
18         System.out.println("結果1: " + result);
19 
20         System.out.println("---------------");
21 
22         result = filter(polices, (Police police) -> "浙江".equals(police.getPoliceNativePlace()));
23         System.out.println("結果2: " + result);
24     }
25 
26     /**
27      * 篩選器
28      * @param container
29      * @param predicate
30      * @return
31      */
32     static <T> List<T> filter(List<T> container, Predicate<T> predicate) {
33         List<T> result = new ArrayList<>();
34 
35         for (T e : container) {
36             if (predicate.test(e)) {
37                 result.add(e);
38             }
39         }
40 
41         return result;
42     }
43 }

    到此,即實現了行為引數化。關於Java8的一些概念和知識點我們再後續在去介紹。我們將開個系列去詳細介紹Java8的使用。