Pattern per un risultato di una chiamata al metodo

3

Molto spesso ho bisogno di chiamare un metodo di servizio da un controller e in base al risultato, se c'è un errore mostra un messaggio di errore appropriato a seconda dell'errore (o qualche altra azione, mostrare il messaggio è solo un esempio), altrimenti procedere.

Un modo molto semplice che sto usando è di restituire un oggetto risultato da tali chiamate di metodo. Qualcosa del genere:

public interface Outcome {
    boolean success();
    String error;
}

public class Success implements Outcome {
    public boolean success() {
        return true;
    }

    public String error() {
        return "";
    }
}

public class Failure implements Outcome {
    String error; //or call it reason

    public Failure(String error) {
        this.error = error;
    }

    public boolean success() {
        return false;
    }

    public String error() {
        return error;
    }
}

E nel controller, verificando result.success () e procedendo in base al risultato.

C'è un modo più elegante / oop di farlo? Forse è come passare il successo e i callback falliti ai metodi di servizio come parametri, o in qualche modo migliore.

lascia che ti fornisca un esempio di codice concreto per chiarire:

Un nuovo utente, sceglie un nome utente per se stesso scrivendolo in un campo di testo, quindi preme il pulsante OK.

  • SUCCESSO: l'utente è registrato
  • FAIL: se c'è già un utente con lo stesso nome, all'utente viene richiesto di scegliere un nome diverso
  • FAIL: qualche altro problema, supponiamo che il sistema non accetti nuove registrazioni al momento
  • FAIL: username è una parola contrassegnata come offensiva

come vedi ci sono diversi risultati per una chiamata al metodo, e per ogni risultato deve essere intrapresa un'azione diversa.

public class UserService {
    public Outcome register(String username)...
}

public class RegistrationController {
    ...
    //somewhere inside controller, there is a handler for OK button's click event

    onclick...
    Outcome outcome = userService.register(username);
    if(outcome.success()) 
        ...
    else {
        if(outcome.error() == "OFFENSIVE_USERNAME"
        //... other outcomes
    }

    ...
}
    
posta Reek 29.12.2016 - 15:23
fonte

2 risposte

2

Ho avuto questa domanda per REST qualche tempo fa, e ho usato questa risposta . Fornisce buone informazioni sull'esito del servizio. È anche scritto in Java; -)

Con questo design puoi scrivere i tuoi metodi di servizio in questo modo: public AbstractResponse getAllUsers() {} . Inoltre, a causa del polimorfismo, puoi aggiungere anche altre risposte. Ad esempio, avevo bisogno di una risposta extra per quando si verifica un avviso, quindi ho creato una classe WarningResponse .

Si noti che è possibile tralasciare l'enum di stato se si sta utilizzando questo solo in Java. Ho convertito i miei oggetti in JSON, quindi avevo bisogno di una sorta di variabile per mostrare lo stato.

    
risposta data 29.12.2016 - 15:52
fonte
1

C'è un modello funzionale comune per gestire gli errori. Di solito si chiama Either. L'idea è di restituire un oggetto che può essere un risultato o un errore, ma non controllare alcun campo booleano in esso e invece affidarsi all'oggetto stesso per elaborare sia il risultato che l'errore:

public final class Either<Left, Right> {
  private final Optional<Left> left;
  private final Optional<Right> right;
  /** @param left - non null value for an erroneous outcome
   **/
  public static <Left, Right> Either<Left, Right> left(Left left) {
    return new Either<>(Optional.of(left), Optional.empty());
  }

  /** @param right - non null value for a normal outcome
   **/
  public static <Left, Right> Either<Left, Right> right(Right right) {
    return new Either<>(Optional.empty(), Optional.of(right));
  }

  /** Accepts handler for both potential outcomes, only one handler is executed
   * This is an imperative terminal operation
   **/
  public void forEach(Consumer<Left> leftConsumer, Consumer<Right> rightConsumer) {
    left.ifPresent(leftConsumer);
    right.ifPresent(rightConsumer);
  }

  /** Accepts processors for each outcome
   *  This implements a functional approach for handling errors
   * @param leftMapper, rightMapper - converters for each outome to target value of type T
   */
  public <T> map(Function<Left, T> leftMapper, Function<Right, T> rightMapper) {
    return left.map(leftMapper).orElseGet(() -> right.map(rightMapper).get());
  }

  private Either(Optional<Left> left, Optional<Right> right) {
    assert left.isPresent() ^ right.isPresent();
    this.left = left;
    this.right = right;
  }
}

class UsageExample {
    public String getUserOrError(int id) {
       return repository.getUser(id).map(
         error -> error.getMessage(), 
         user -> user.getName()
       );
    }
}

Guarda come nell'esempio di utilizzo non vengono eseguiti controlli: sia l'errore che il risultato normale sono trattati in modo uniforme, ma in modo sicuro.

Esiste un equivalente di Either di Scala in Java 8?

    
risposta data 29.12.2016 - 17:24
fonte

Leggi altre domande sui tag