公子奇带你一步一步了解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的使用。