我想說的規則是,如果一個lambda表達式是如此複雜,你覺得有必要模擬出它的位,這可能是太複雜了。它應該被分解成更小的部分組合在一起,或者模型需要進行調整以使其更易於合成。
我會說,Andrey Chaschev's answer這表明參數化依賴是一個很好的,可能適用於某些情況。所以,+1。一個可以繼續這一進程,並分解加工成更小的碎片,就像這樣:
public List<Item> processFile(
String fileName,
Function<String, BufferedReader> toReader,
Function<BufferedReader, List<String>> toStringList,
Function<List<String>, List<Item>> toItemList)
{
List<String> lines = null;
try (BufferedReader br = toReader.apply(fileName)) {
lines = toStringList.apply(br);
} catch (IOException ioe) { /* ... */ }
return toItemList.apply(lines);
}
一對夫婦的意見這一點,雖然。首先,由於各種lambda投擲討厭IOExceptions
,並且Function
類型沒有被聲明爲拋出該異常,所以這不起作用。第二個是你必須傳遞給這個函數的lambdas是可怕的。儘管這是行不通的(因爲檢查的例外),但我把它寫出來了:
void processAnActualFile() {
List<Item> items = processFile(
"file.csv",
fname -> new BufferedReader(new FileReader(fname)),
// ERROR: uncaught IOException
br -> {
List<String> result = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
result.add(line);
}
return result;
}, // ERROR: uncaught IOException
stringList -> {
List<Item> result = new ArrayList<>();
for (String line : stringList) {
result.add(new Item(line));
}
return result;
});
}
呃!我想我已經發現了新的代碼味道:
如果你必須在lambda中編寫for-loop或while循環,那麼你做錯了什麼。
這裏有一些事情正在進行。首先,I/O庫實際上由緊密耦合的不同實現部分(InputStream
,Reader
,BufferedReader
)組成。試圖將它們分開是沒有用的。事實上,圖書館已經發展,因此有一些便利工具(例如NIO Files.readAllLines
)能夠爲您處理一大堆腿部工作。
更重要的一點是設計函數在它們之間傳遞值的集合(列表)並構造這些函數實際上是錯誤的方法。它導致每個函數都必須在它內部編寫一個循環。我們真正想要做的是編寫函數,每個函數都在一個值上運行,然後讓Java 8中的新Streams庫負責我們的聚合。
從評論「做更多的魔法」所描述的代碼中提取的關鍵功能是將List<String>
轉換成List<Item>
。我們想抽取轉換一個String
到Item
計算,像這樣:
class Item {
static Item fromString(String s) {
// do a little bit of magic
}
}
一旦你有了這個,那麼你就可以讓流和NIO庫做了一堆的工作適合你:
public List<Item> processFile(String fileName) {
try (Stream<String> lines = Files.lines(Paths.get(fileName))) {
return lines.map(Item::fromString)
.collect(Collectors.toList());
} catch (IOException ioe) {
ioe.printStackTrace();
return Collections.emptyList();
}
}
(請注意,這個簡短方法的更多一半是用於處理IOException
。)
現在,如果你想做一些單元測試,你真正需要測試的是一點魔法。所以,你把它包裝成不同的流管道,像這樣:
void testItemCreation() {
List<Item> result =
Arrays.asList("first", "second", "third")
.stream()
.map(Item::fromString)
.collect(Collectors.toList());
// make assertions over result
}
(其實,即使這是不完全正確你想要編寫單元測試單行轉換成一個單一的Item
不過。也許你有一些測試數據的地方,所以你將它轉換爲這樣的項目列表,然後在該列表中的結果條目的關係,使全球的斷言。)
我徘徊這與您如何拆分lambda的原始問題相去甚遠。請原諒我放縱自己。
原始示例中的lambda非常不幸,因爲Java I/O庫非常麻煩,並且NIO庫中有新的API將示例轉換爲單行內容。
但是,這裏的教訓是,不是編寫處理聚合的函數,而是編寫處理各個值的函數,並讓流處理聚合。通過這種方式,您可以通過以不同方式將流管線插入到一起來測試,而不是通過模擬複雜lambda的位來進行測試。
一個lambda可以像其他任何東西一樣加載上下文(如果你正在尋找它的內部存根)。但要注意,沒有捕獲變量的lambda最有可能是單身。 – aepurniet