豬腳:以下內(nèi)容參考《Java 8 in Action》
發(fā)布:https://ryan-miao.github.io/2017/07/15/java8-in-action-2/
源碼:github
需求
果農(nóng)需要篩選蘋(píng)果,可能想要綠色的,也可能想要紅色的,可能想要大蘋(píng)果(>150g),也可能需要紅的大蘋(píng)果?;诖说葪l件,編寫(xiě)篩選的代碼。
1. 策略模式解決方案
1.1 最直觀的做法
首先,已知信息是一筐蘋(píng)果(List<Apple> inventory
),但篩選條件多種多樣。我們可以根據(jù)不同的條件寫(xiě)不同的方法來(lái)達(dá)到目的。比如,找出綠色的蘋(píng)果:
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; }
同樣的,可以編寫(xiě)filterRed
, filterWeight
等等。但必然出現(xiàn)重復(fù)代碼,違反軟件工程原則Don't repeast yourself
。而且,篩選的類也會(huì)顯得臃腫。
現(xiàn)在,有一種更容易維護(hù),更容易閱讀的策略模式來(lái)實(shí)現(xiàn)這個(gè)需求。
1.2 策略模式
由于多種篩選條件的結(jié)果都是返回一個(gè)boolean
值,那么可以把這個(gè)條件抽取出來(lái),然后在篩選的時(shí)候傳入條件。這個(gè)篩選條件叫做謂詞。
創(chuàng)建謂詞接口:
public interface ApplePredicate { boolean test(Apple apple); }
添加幾個(gè)判斷條件:
public class AppleGreenColorPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { return "green".equals(apple.getColor()); } }public class AppleHeavyWeightPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { return apple.getWeight() > 150; } }public class AppleRedAndHeavyPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { return "red".equals(apple.getColor()) && apple.getWeight() >150; } }
篩選的方法:
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate predicate){ List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (predicate.test(apple)){ result.add(apple); } } return result; }
這樣,我們就可以根據(jù)不同的條件進(jìn)行篩選了。
List<Apple> inventory = new ArrayList<>(); inventory.add(new Apple("red", 100)); inventory.add(new Apple("red", 200)); inventory.add(new Apple("green", 200)); List<Apple> redHeavyApples = filterApples(inventory, new AppleRedAndHeavyPredicate()); Assert.assertEquals(1, redHeavyApples.size()); Assert.assertEquals(200, redHeavyApples.get(0).getWeight());
以上的代碼設(shè)計(jì)方案幾乎是最好理解和擴(kuò)展的了,當(dāng)條件發(fā)生改變的時(shí)候只要增加一個(gè)類就可以。但java8提供了更好的選擇,一種你只要聲明一個(gè)接口,具體實(shí)現(xiàn)不用管,只有當(dāng)使用的時(shí)候才去關(guān)心。
1.3 方法傳遞
java8提供了把方法當(dāng)做參數(shù)傳遞的能力。這樣,上面的代碼就可以這樣寫(xiě):
List<Apple> apples = filterApples(inventory, apple -> "red".equals(apple.getColor()) && apple.getWeight() > 150); Assert.assertEquals(1, apples.size()); Assert.assertEquals(200, apples.get(0).getWeight());
除了接口聲明,不需要實(shí)現(xiàn)接口的類。我們只需要傳入一個(gè)類似匿名內(nèi)部類的東西,是的,lambda表達(dá)式和匿名內(nèi)部類是可以互相轉(zhuǎn)換的。
如此,我們?cè)O(shè)計(jì)接口的時(shí)候只要聲明一個(gè)接口作為參數(shù),然后再調(diào)用的時(shí)候把邏輯當(dāng)做參數(shù)傳進(jìn)去。這個(gè)在我看來(lái)就是傳遞方法了。就像Javascript,可以把一個(gè)方法當(dāng)做參數(shù)。
與之前的設(shè)計(jì)模式相比,lambda可以不用寫(xiě)那么類。
1.4 新需求
現(xiàn)在,果農(nóng)需要包裝蘋(píng)果。包裝的方式有多種,我將包裝的結(jié)果打印出來(lái),就是打印的樣式也有多種。比如:
A light green apple
或者
An apple of 150g
上面是兩種打印方式,按照之前的策略模式需要?jiǎng)?chuàng)建兩個(gè)類。下面采用lambda來(lái)實(shí)現(xiàn)。
public interface AppleFormatter { String format(Apple apple); }public class AppleOutput{ public static void prettyPrintApple(List<Apple> inventory, AppleFormatter formatter){ for (Apple apple : inventory) { String format = formatter.format(apple); System.out.println(format); } } public static void main(String[] args){ List<Apple> inventory = new ArrayList<>(); inventory.add(new Apple("red", 100)); inventory.add(new Apple("red", 200)); inventory.add(new Apple("green", 200)); prettyPrintApple(inventory, new AppleFormatter() { @Override public String format(Apple apple) { String characteristic = apple.getWeight()>150?"heavy":"light"; return "A " + characteristic + " " + apple.getColor() + " apple."; } }); prettyPrintApple(inventory, apple -> "An apple of " + apple.getWeight() + "g"); } }
控制臺(tái)打印:
A light red apple. A heavy red apple. A heavy green apple. An apple of 100g An apple of 200g An apple of 200g
如果使用IntelIJ IDEA作為編輯器,那么肯定會(huì)忍受不了匿名內(nèi)部類,因?yàn)镮DEA會(huì)不停的提示你:匿名內(nèi)部類可以轉(zhuǎn)變?yōu)榉椒▍?shù)。
1.5 更普遍的用法
上面的篩選只是針對(duì)Apple的,那么是否可以推廣開(kāi)來(lái)呢?下面針對(duì)List類型抽象化來(lái)構(gòu)造篩選條件。
創(chuàng)建一個(gè)條件接口:
public interface Predicate<T> { boolean test(T t); }
更新一個(gè)更普遍的filter:
public static <T> List<T> filter(List<T> list, Predicate<T> p){ List<T> result = new ArrayList<T>(); for (T e : list) { if (p.test(e)){ result.add(e); } } return result; }
那么,可能這樣用:
public static void main(String[] args) { List<Apple> appleList = new ArrayList<>(); appleList.add(new Apple("red", 100)); appleList.add(new Apple("red", 160)); appleList.add(new Apple("green", 60)); List<Apple> redApples = filter(appleList, (Apple apple) -> "red".equals(apple.getColor())); Assert.assertEquals(2, redApples.size()); List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7,8,9); List<Integer> lessThan4Numbers = filter(numberList, (Integer num) -> num < 4); Assert.assertEquals(3, lessThan4Numbers.size()); }
1.6 排序
行為參數(shù)化的過(guò)程掌握后,很多東西就會(huì)自然而然的使用了。比如排序。果農(nóng)需要將蘋(píng)果按照大小排序呢?
java8中List是有默認(rèn)方法的:
default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }
其實(shí)就是將以前手動(dòng)排序封裝了。那么,蘋(píng)果的排序就可以傳入一個(gè)比較器實(shí)現(xiàn):
@Testpublic void sort(){ List<Apple> appleList = new ArrayList<>(); appleList.add(new Apple("red", 100)); appleList.add(new Apple("red", 160)); appleList.add(new Apple("green", 60)); appleList.sort((o1, o2) -> o1.getWeight()-o2.getWeight()); }
根據(jù)IDEA的提示,進(jìn)一步:
appleList.sort(Comparator.comparingInt(Apple::getWeight));
這里就涉及了多次行為傳參了。后面再說(shuō)。
1.7 Runnable
多線程Runnable的時(shí)候經(jīng)常會(huì)采用匿名內(nèi)部類的做法:
@Testpublic void testRunnable(){ Runnable runnable = new Runnable() { @Override public void run() { System.out.println("running"); } }; new Thread(runnable).start(); }
采用lambda行為傳參就變?yōu)椋?/p>
@Testpublic void testRunnable(){ Runnable runnable = () -> System.out.println("running"); new Thread(runnable).start(); }
小結(jié)
本次測(cè)試主要理解如下內(nèi)容:
行為參數(shù)化,就是一個(gè)方法接受多個(gè)不同的行為作為參數(shù),并在內(nèi)部使用它們,完成不同行為的能力。
傳遞代碼,就是將行為作為參數(shù)傳遞給方法。
http://www.cnblogs.com/woshimrf/p/java8-in-action-2.html