2015-03-20 14 views
5

Sto cercando un modo pulito per tradurre condizioni logiche complesse con if e else dichiarazioni che portano a diverse azioni, in lambda e corsi d'acqua.Tradurre complessa logica condizionale all'interno di un ciclo in torrenti e lambda

Supponiamo che io ho questo codice:

List<OuterData> result = new LinkedList<>(); 

for (Outer outer : getOutersFromSomewhere()) { 
    OuterData outerData = new OuterData(); 

    if (outer.isImportant()) { 
     doImportantAction(outer, outerData); 
    } else if (outer.isTrivial()) { 
     doTrivialAction(outer, outerData); 
    } else { 
     doDefaultAction(outer, outerData); 
    } 

    for (Inner inner : outer.getInners()) { 
     if (inner.mustBeIncluded()) { 
      InnerData innerData = new InnerData(); 

      if (inner.meetsCondition1()) { 
       doAction1(inner, innerData, outer, outerData); 
      } else if (inner.meetsCondition2()) { 
       doAction2(inner, innerData, outer, outerData); 
      } else { 
       doDefaultAction(inner, innerData, outer, outerData); 
      } 
      outerData.add(innerData); 
     } 
    } 
    result.add(outerData); 
} 

return result; 

Questo è semplificato dal codice vero che ho. So che può essere ottimizzato e refactored, cioè potrei spostare interno for in un metodo private. Mi piacerebbe sapere come tradurre le parti di if, else if e else in stream e lambda.

So come tradurre lo scheletro di questo esempio. Vorrei usare List.stream(), Stream.map(), Stream.filter(), Stream.collect() e Stream.peek(). Il mio problema riguarda solo le filiali condizionali. Come posso fare questa traduzione?

+2

Potrebbe essere possibile semplificare il codice nell'esempio. –

+0

@RaulGuiu OK Lo farò. Grazie –

+1

Penso che potresti usare alcuni filtri diversi con i condizionali if e un foreach con il corpo del if. –

risposta

5

Un primo modo ovvio è far scorrere i tuoi elementi, filtrarli secondo i criteri necessari e quindi applicare l'azione su ogni elemento rimanente. Questo rende anche il codice più pulito:

List<Outer> outers = getOutersFromSomewhere(); 
outers.stream().filter(Outer::isImportant) 
    .forEach(outer -> doImportantAction(outer, outerDate)); 
outers.stream().filter(Outer::isTrivial) 
    .forEach(outer -> doTrivialAction(outer, outerDate)); 
// default action analog 

Attenzione: questo funziona solo se l'importante, il banale, e gli elementi predefiniti formano una partizione . Altrimenti non è equivalente alla tua if-else-struttura. Ma forse questo è inteso comunque ...

Il problema principale con questo approccio: non è molto buono OOP. Stai interrogando gli oggetti per prendere una decisione. Ma OOP dovrebbe essere "dire, non chiedere" il più possibile.

Così Un'altra soluzione è quella di fornire un metodo che consumano nella vostra Outer classe:

public class Outer { 
    ... 
    public void act(OuterData data, Consumer<Outer> importantAction, 
      Consumer<Outer> trivialAction, Consumer<Outer> defaultAction) { 
     if (isImportant()) 
      importantAction.accept(this, data); 
     else if (isTrivial()) 
      trivialAction.accept(this, data); 
     else 
      defaultAction.accept(this, data); 
    } 
} 

Ora si chiamano semplice come questo:

List<Outer> outers = getOutersFromSomewhere(); 
outers.forEach(outer -> outer.act(...)); // place consumers here (lambdas) 

Questo ha un chiaro vantaggio: Se mai aggiungere una funzionalità alla classe Outer - diciamo isComplex() - è necessario modificare solo gli interni di quella singola classe (e magari risolvere l'errore del compilatore in altre parti). O forse puoi aggiungere questa funzionalità in un modo compatibile con le versioni precedenti.

Le stesse regole possono essere applicate alla classe Inner e l'iterazione.