《Java8实战》笔记(02):通过行为参数传递代码
本文源碼
應(yīng)對(duì)不斷變化的需求
通過(guò)篩選蘋果闡述通過(guò)行為參數(shù)傳遞代碼
初試牛刀:篩選綠蘋果
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; }要是農(nóng)民想要篩選多種顏色:淺綠色、暗紅色、黃色等,這種方法就應(yīng)付不了了。一個(gè)良好的原則是在編寫類似的代碼之后,嘗試將其抽象化。
再展身手:把顏色作為參數(shù)
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; }運(yùn)用
List<Apple> greenApples = filterApplesByColor(inventory, "green"); List<Apple> redApples = filterApplesByColor(inventory, "red");“要是能區(qū)分輕的蘋果和重的蘋果就太好了。重的蘋果一般是重量大于150克。”
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; }發(fā)現(xiàn)有重復(fù)代碼,打破DRY(Don’t Repeat Yourself)原則
第三次嘗試:對(duì)你能想到的每個(gè)屬性做篩選
public static List<Apple> filterApples(List<Apple> inventory, int weight, String color, boolean flag){List<Apple> result = new ArrayList<Apple>();for (Apple apple: inventory){if ( (flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight) ){result.add(apple);}}return result; }你可以這么用(但真的很笨拙)
List<Apple> greenApples = filterApples(inventory, "green", 0, true); List<Apple> heavyApples = filterApples(inventory, "", 150, false);行為參數(shù)化
定義一個(gè)接口來(lái)對(duì)選擇標(biāo)準(zhǔn)建模:
interface ApplePredicate{public boolean test(Apple a); }現(xiàn)在你就可以用ApplePredicate的多個(gè)實(shí)現(xiàn)代表不同的選擇標(biāo)準(zhǔn)了
public class AppleWeightPredicate implements ApplePredicate{public boolean test(Apple apple){return apple.getWeight() > 150; } }public class AppleColorPredicate implements ApplePredicate{public boolean test(Apple apple){return "green".equals(apple.getColor());} }這里有 策略模式 的影子
策略模式:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。
該怎么利用ApplePredicate的不同實(shí)現(xiàn)呢?你需要filterApples方法接受ApplePredicate對(duì)象,對(duì)Apple做條件測(cè)試。這就是行為參數(shù)化:讓方法接受多種行為(或戰(zhàn)略)作為參數(shù),并在內(nèi)部使用,來(lái)完成不同的行為。
第四次嘗試:根據(jù)抽象條件篩選
public static List<Apple> filter(List<Apple> inventory, ApplePredicate p){List<Apple> result = new ArrayList<>();for(Apple apple : inventory){if(p.test(apple)){result.add(apple);}}return result; }傳遞代碼/行為
public class AppleRedAndHeavyPredicate implements ApplePredicate{public boolean test(Apple apple){return "red".equals(apple.getColor()) && apple.getWeight() > 150; } }List<Apple> redAndHeavyApples = filter(inventory, new AppleRedAndHeavyPredicate());filterApples方法的行為取決于你通過(guò)ApplePredicate對(duì)象傳遞的代碼。換句話說(shuō),你把filterApples方法的行為參數(shù)化了!
多種行為,一個(gè)參數(shù)
行為參數(shù)化的好處在于你可以把迭代要篩選的集合的邏輯與對(duì)集合中每個(gè)元素應(yīng)用的行為區(qū)分開(kāi)來(lái)。這樣你可以重復(fù)使用同一個(gè)方法,給它不同的行為來(lái)達(dá)到不同的目的。
編寫靈活的prettyPrintApple方法
編寫一個(gè)prettyPrintApple方法,它接受一個(gè)Apple的List,并可以對(duì)它參數(shù)化,以多種方式根據(jù)蘋果生成一個(gè)String輸出
public static void prettyPrintApple(List<Apple> inventory, ???){for(Apple apple: inventory) {String output = ???.???(apple);System.out.println(output);} }首先
public interface AppleFormatter{String accept(Apple a); }然后
public class AppleFancyFormatter implements AppleFormatter{public String accept(Apple apple){String characteristic = apple.getWeight() > 150 ? "heavy" : "light";return "A " + characteristic + " " + apple.getColor() +" apple";} }public class AppleSimpleFormatter implements AppleFormatter{public String accept(Apple apple){return "An apple of " + apple.getWeight() + "g";} }最后
public static void prettyPrintApple(List<Apple> inventory, AppleFormatter formatter){for(Apple apple: inventory){String output = formatter.accept(apple);System.out.println(output);} }運(yùn)用
prettyPrintApple(inventory, new AppleFancyFormatter()); prettyPrintApple(inventory, new AppleSimpleFormatter());輸出
A light green apple A heavy red apple …An apple of 80g An apple of 155g …
對(duì)付啰嗦
人們都不愿意用那些很麻煩的功能或概念。目前,當(dāng)要把新的行為傳遞給filterApples方法的時(shí)候,你不得不聲明好幾個(gè)實(shí)現(xiàn)ApplePredicate接口的類,然后實(shí)例化好幾個(gè)只會(huì)提到一次的ApplePredicate對(duì)象。這真是很啰嗦,很費(fèi)時(shí)間!
匿名類
匿名類和你熟悉的Java局部類(塊中定義的類)差不多,但匿名類沒(méi)有名字。它允許你同時(shí)聲明并實(shí)例化一個(gè)類。換句話說(shuō),它允許你隨用隨建。
第五次嘗試:使用匿名類
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {public boolean test(Apple apple){return "red".equals(apple.getColor());} });但匿名類還是不夠好。第一,它往往很笨重,因?yàn)樗加昧撕芏嗫臻g。第二,很多程序員覺(jué)得它用起來(lái)很讓人費(fèi)解。
鼓勵(lì)程序員使用行為參數(shù)化模式,通過(guò)引入Lambda表達(dá)式——一種更簡(jiǎn)潔的傳遞代碼的方式。
第六次嘗試:使用Lambda表達(dá)式
List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));第七次嘗試:將List類型抽象化
目前,filterApples方法還只適用于Apple。還可以將List類型抽象化,從而超越你眼前要處理的問(wèn)題。
public interface Predicate<T>{boolean test(T t); }public static <T> List<T> filter(List<T> list, Predicate<T> p){List<T> result = new ArrayList<>();for(T e: list){if(p.test(e)){result.add(e);}}return result; }List<Apple> redApples = filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);
真實(shí)的例子
用Comparator來(lái)排序
// java.util.Comparator public interface Comparator<T> {public int compare(T o1, T o2); }inventory.sort(new Comparator<Apple>() {@Overridepublic int compare(Apple o1, Apple o2) {return o1.getWeight().compareTo(o2.getWeight());} });inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));用Runnable執(zhí)行代碼塊
//行為參數(shù)化 Thread t = new Thread(new Runnable() { @Overridepublic void run() {System.out.println("Hello, World!");} });t = new Thread(()->System.out.println("Hello, World!")) ;GUI 事件處理
Button button = new Button("Send"); button.setOnAction(new EventHandler<ActionEvent>() {public void handle(ActionEvent event) {label.setText("Sent!!");} });//行為參數(shù)化 button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));小結(jié)
- 行為參數(shù)化,就是一個(gè)方法接受多個(gè)不同的行為作為參數(shù),并在內(nèi)部使用它們,完成不同行為的能力。
- 行為參數(shù)化可讓代碼更好地適應(yīng)不斷變化的要求,減輕未來(lái)的工作量。
- 傳遞代碼,就是將新行為作為參數(shù)傳遞給方法。但在Java 8之前這實(shí)現(xiàn)起來(lái)很啰嗦。為接口聲明許多只用一次的實(shí)體類而造成的啰嗦代碼,在Java 8之前可以用匿名類來(lái)減少。
- Java API包含很多可以用不同行為進(jìn)行參數(shù)化的方法,包括排序、線程和GUI處理。
總結(jié)
以上是生活随笔為你收集整理的《Java8实战》笔记(02):通过行为参数传递代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python(14)-模块
- 下一篇: MachineLearning(4)-核