1. 程式人生 > >學習Java 8的編碼風格帶來什麼樣的好處

學習Java 8的編碼風格帶來什麼樣的好處

表現力是功能風格程式設計的好處之一,但是這對你的程式碼意味著什麼?在本文中,我們比較命令式和功能式程式碼的例子,識別表達性和簡潔性。你會看到這些特質是如何支援可讀性的,你會考慮一個相反的例子:當簡潔的要求導致無用的程式碼。最後,我們考察Java 8在函式合成中垂直對齊點的習慣。雖然這個約定對某些Java開發人員可能並不熟悉,但是一個簡單的例子說明了它的價值。關於java8

Java 8是對Java語言最重要的更新,自啟動以來,包含了大量新功能,您可能會想知道從哪裡開始。在這個系列中,作者和教育家Venkat Subramaniam提供了Java 8的一種慣用方法:簡短的探索,邀請您重新思考您認為理所當然的Java慣例,同時逐漸將新技術和語法整合到您的程式中。

令人驚訝的結果

在Java 8釋出大約一年之後,我在我的網站上釋出了一個簡短而不受控制的調查,邀請開發者參與。每個參與者都在命令式或功能式中呈現一段程式碼,然後要求確定程式碼的行為。我測量了每個訪問者產生響應的時間,比較了兩個不同程式碼示例的結果。這次調查是開放了48小時,當時有超過1100人蔘加。結果包含了一些驚喜。

大多數開發人員,都有很多豐富的程式設計風格經驗。儘管功能風格已經存在了很長時間,但是對於大多數Java程式設計師來說卻並不熟悉。瞭解到這一點,82%的接受命令式編碼的調查物件能夠確定其正確的行為並不奇怪。與此同時,只有百分之七十五的獲得功能風格程式碼的受訪者得到了正確的答案。

然而,令我感到意外的是受訪者瞭解這兩個程式碼樣本的時間。計算命令式程式碼的中位時間比功能式程式碼的中位時間長30秒。

在家嘗試這個實驗

如果寫得很好,功能風格的程式碼比命令風格的程式碼更具表現力和簡潔性

我們來看下面這2個例子

命令性程式碼示例
List<String> names = Arrays.asList("Jack"
, "Jill", "Nate", "Kara", "Kim", "Jullie", "Paul", "Peter");
List<String> subList = new ArrayList<>();
for(String name : names) {
 if(name.length() == 4)
   subList.add(name);
}
StringBuilder namesOfLength4 = new StringBuilder();
for(int i = 0; i < subList.size() - 1; i++) {
 namesOfLength4.append
(subList.get(i));
 namesOfLength4.append(", ");
}
if(subList.size() > 1)
 namesOfLength4.append(subList.get(subList.size() - 1));
System.out.println(namesOfLength4);

你需要多少時間來弄清這個程式碼?如果花費的時間比您預期的要長,請不要感到驚訝。這不代表你的能力,程式碼質量差。

現在用Java 8支援的功能風格編寫等效示例:

List<String> names = Arrays.asList("Jack", "Jill", "Nate", "Kara", "Kim", "Jullie", "Paul", "Peter");

System.out.println(
 names.stream()
   .filter(name -> name.length() == 4)
   .collect(Collectors.joining(", ")));

需要多少時間來弄清楚這個程式碼?顯然,你已經確定了目的,所以這不是一個真正的實驗。如果你想真的比較樣本,請問幾個同事找出一個程式碼樣本或其他,然後比較他們的響應時間。

為什麼功能風格的編碼很重要

如果您熟悉Java 8,那麼您可能無需弄清楚第2個例子中的程式碼。即使您對Java 8不熟悉,也可以使用描述性方法名稱。您還可以快速理解程式碼,因為它比例子1簡潔得多。

從本質上講,程式碼說:給定一個名稱集合,只選擇長度為4的名稱,然後用逗號連線它們。

這個例子是人為設計的,但它確實說明了編碼的簡潔性和表達性的價值。我們在函式式程式碼中比在命令式程式碼中看到更多這些特性。

編寫可讀的程式碼

功能風格的程式碼是表達性和簡潔性的,這導致程式既短又易於閱讀。再看一個例子:

命令性程式碼示例
int result = 0;
for(int e : numbers) {
 if(e > 3 && e % 2 == 0 && e < 8) {
   result += e * 2;
 }
}
System.out.println(result);
給定列表呼叫numbers,這個程式碼執行一個大於3小於8的偶數的和,然後列印結果。程式碼由七條線組成,我們可以減少一兩條線。
現在用Java 8支援的功能風格編寫等效示例:
System.out.println(
numbers.stream()
  .filter(e -> e  > 3)
  .filter(e -> e % 2 == 0)
  .filter(e -> e < 8)
  .mapToInt(e -> e * 2)
  .sum());
這也是七行程式碼,但在這種情況下,進一步減少程式碼是沒有幫助的。

功能風格的程式碼並不總是比命令風格的程式碼短。更重要的是它具有表現力。程式碼簡潔但難於閱讀是沒有幫助的。

不要犯這個錯誤

功能風格的程式碼被設計成比命令式程式碼更簡潔,但是不能確保它更具可讀性。看看下面這個例子就知道了。

System.out.println(
names.stream().
filter(name -> name.startsWith("J")).
filter(name -> name.length() > 3)
 .map(name -> name.toUpperCase()).collect(Collectors.joining(", ")));

這個例子中,filtermap和其它功能元件增加了程式碼的表現力。但是你可能會注意到這個程式碼更簡潔。

雖然只有兩行,但這些程式碼仍然需要相當大的努力來閱讀和理解。你的眼睛緊張,看看一個函式呼叫結束,下一個開始。程式碼非常簡短,但是寫得很簡單。編寫這種無用的程式碼只有一個理由:開發人員必須憎恨與他們一起工作的每個人。

使你的程式碼簡潔,不簡單

在程式設計中,我們很容易忽視表現力和可讀性的價值。Java 8通過慣例鼓勵這些特性,這表明我們為了函式組合而垂直對齊點。

不幸的是,即使在多次提醒之後,程式設計師經常忽略這個約定。更有經驗的程式設計師應該在程式碼審查期間強制執行這個約定。

下面這個例子使用對齊約定重寫上面例子的程式碼時會發生什麼情況:

Java中的函式組合:
System.out.println(
names.stream()
     .filter(name -> name.startsWith("J"))
     .filter(name -> name.length() > 3)
     .map(name -> name.toUpperCase())
     .collect(Collectors.joining(", ")));
這裡我們看到例5中的簡潔程式碼,但這些點是垂直對齊的,而且我們拒絕將多個條件組合成一個引數的衝動。因此,每條路線都是凝聚力:狹隘和專注,只有一個明確的任務。
一個有用的約定

雖然這看起來可能是可有可無的,但遵循Java 8的對齊約定是非常有益的。

  • 遵循這個約定的程式碼更容易閱讀,理解和解釋。在詳細考察每個部分之前,我們可以迅速掌握總體目標。

  • 元素清晰,易於定位,支援更快的修改。如果我們想要包含另一個條件,或者刪除或修改一個現有條件,找到這條線並進行更改將會相對簡單。

  • 程式碼更容易維護,這表示我們關心我們團隊中的其他開發人員。編寫有用的程式碼可以極大地影響團隊的士氣,除了使程式碼更容易維護。

結論

保持程式碼的每一行簡明扼要是一個很好的做法,但是過度會導致程式碼變得簡單而難以閱讀。為了提高表達能力,問問自己程式碼是否容易理解。為了提高可讀性,應用Java 8的垂直排列點約定。使用這些簡單的技巧,您將建立簡潔,富有表現力和可讀性的功能風格的程式碼。

原文:https://www.ibm.com/developerworks/java/library/j-java8idioms4/index.html

翻譯和整理:劉志鵬

推薦閱讀
?

JAVA葵花寶典

?

長按關注置頂

java知識和技術查漏補缺,空餘時間學習碎片化知識,分享開發、運維、架構等綜合性知識,助力職場最後一公里與職業進階,每天看寶典,就選它。