說明:因為本公子一直從事監獄軟件開發,所以本系列博客的引入也以此為背景。問題做了簡化,只是為了來講解技術點。
一、問題提出
今日在好好的擼着代碼,超哥(民警)找來了,讓把監獄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的使用。