《Java8实战》笔记(02):通过行为参数传递代码
本文源碼
應對不斷變化的需求
通過篩選蘋果闡述通過行為參數傳遞代碼
初試牛刀:篩選綠蘋果
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; }要是農民想要篩選多種顏色:淺綠色、暗紅色、黃色等,這種方法就應付不了了。一個良好的原則是在編寫類似的代碼之后,嘗試將其抽象化。
再展身手:把顏色作為參數
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; }運用
List<Apple> greenApples = filterApplesByColor(inventory, "green"); List<Apple> redApples = filterApplesByColor(inventory, "red");“要是能區分輕的蘋果和重的蘋果就太好了。重的蘋果一般是重量大于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; }發現有重復代碼,打破DRY(Don’t Repeat Yourself)原則
第三次嘗試:對你能想到的每個屬性做篩選
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);行為參數化
定義一個接口來對選擇標準建模:
interface ApplePredicate{public boolean test(Apple a); }現在你就可以用ApplePredicate的多個實現代表不同的選擇標準了
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());} }這里有 策略模式 的影子
策略模式:定義一系列的算法,把它們一個個封裝起來, 并且使它們可相互替換。
該怎么利用ApplePredicate的不同實現呢?你需要filterApples方法接受ApplePredicate對象,對Apple做條件測試。這就是行為參數化:讓方法接受多種行為(或戰略)作為參數,并在內部使用,來完成不同的行為。
第四次嘗試:根據抽象條件篩選
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方法的行為取決于你通過ApplePredicate對象傳遞的代碼。換句話說,你把filterApples方法的行為參數化了!
多種行為,一個參數
行為參數化的好處在于你可以把迭代要篩選的集合的邏輯與對集合中每個元素應用的行為區分開來。這樣你可以重復使用同一個方法,給它不同的行為來達到不同的目的。
編寫靈活的prettyPrintApple方法
編寫一個prettyPrintApple方法,它接受一個Apple的List,并可以對它參數化,以多種方式根據蘋果生成一個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);} }運用
prettyPrintApple(inventory, new AppleFancyFormatter()); prettyPrintApple(inventory, new AppleSimpleFormatter());輸出
A light green apple A heavy red apple …An apple of 80g An apple of 155g …
對付啰嗦
人們都不愿意用那些很麻煩的功能或概念。目前,當要把新的行為傳遞給filterApples方法的時候,你不得不聲明好幾個實現ApplePredicate接口的類,然后實例化好幾個只會提到一次的ApplePredicate對象。這真是很啰嗦,很費時間!
匿名類
匿名類和你熟悉的Java局部類(塊中定義的類)差不多,但匿名類沒有名字。它允許你同時聲明并實例化一個類。換句話說,它允許你隨用隨建。
第五次嘗試:使用匿名類
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {public boolean test(Apple apple){return "red".equals(apple.getColor());} });但匿名類還是不夠好。第一,它往往很笨重,因為它占用了很多空間。第二,很多程序員覺得它用起來很讓人費解。
鼓勵程序員使用行為參數化模式,通過引入Lambda表達式——一種更簡潔的傳遞代碼的方式。
第六次嘗試:使用Lambda表達式
List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));第七次嘗試:將List類型抽象化
目前,filterApples方法還只適用于Apple。還可以將List類型抽象化,從而超越你眼前要處理的問題。
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);
真實的例子
用Comparator來排序
// 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執行代碼塊
//行為參數化 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!!");} });//行為參數化 button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));小結
- 行為參數化,就是一個方法接受多個不同的行為作為參數,并在內部使用它們,完成不同行為的能力。
- 行為參數化可讓代碼更好地適應不斷變化的要求,減輕未來的工作量。
- 傳遞代碼,就是將新行為作為參數傳遞給方法。但在Java 8之前這實現起來很啰嗦。為接口聲明許多只用一次的實體類而造成的啰嗦代碼,在Java 8之前可以用匿名類來減少。
- Java API包含很多可以用不同行為進行參數化的方法,包括排序、線程和GUI處理。
總結
以上是生活随笔為你收集整理的《Java8实战》笔记(02):通过行为参数传递代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python(14)-模块
- 下一篇: java美元兑换,(Java实现) 美元