Supponiamo di avere un flusso di cose e voglio "arricchirle" a metà stream, posso usare peek()
per fare ciò, ad esempio:
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
Supponiamo che la mutazione delle cose a questo punto nel codice sia un comportamento corretto; ad esempio, il metodo thingMutator
può impostare il campo "lastProcessed" sull'ora corrente.
Tuttavia, peek()
nella maggior parte dei contesti significa "guarda, ma non toccare".
L'utilizzo di peek()
per muta elementi di flusso è antipattern o sconsiderato?
Modifica:
L'approccio alternativo, più convenzionale, sarebbe quello di convertire il consumatore:
private void thingMutator(Thing thing) {
thing.setLastProcessed(System.currentTimeMillis());
}
a una funzione che restituisce il parametro:
private Thing thingMutator(Thing thing) {
thing.setLastProcessed(currentTimeMillis());
return thing;
}
e usa map()
invece:
stream.map(this::thingMutator)...
Ma questo introduce il codice superficiale (il return
) e non sono convinto che sia più chiaro, perché sai che peek()
restituisce lo stesso oggetto, ma con map()
non è nemmeno chiaro a colpo d'occhio che è lo stesso classe di oggetto.
Inoltre, con peek()
puoi avere un lambda che muta, ma con map()
devi costruire un naufragio del treno. Confronto:
stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)
Penso che la versione peek()
sia più chiara e che la lambda si stia chiaramente mutando, quindi non ci sono effetti collaterali "misteriosi". Allo stesso modo, se viene utilizzato un riferimento al metodo e il nome del metodo implica chiaramente la mutazione, anche questo è chiaro ed evidente.
Su una nota personale, non evito di usare peek()
per mutare - lo trovo molto comodo.