Ich habe den nächsten Zweifel. Nach bewährten Methoden von Java, wie man die Fälle verwaltet, in denen das Objekt nicht gefunden werden kann, und wir möchten wissen, warum.

Wenn beispielsweise jemand Probleme beim Anmelden in unserem System hat und wir ihn genau über das Problem informieren möchten, können wir nicht null zurückgeben, da wir den Grund dafür verlieren, dass wir uns nicht anmelden können. Beispiel:

public User login(String username, String password) {
    boolean usernameEmpty = (credentials.getUsername()==null || credentials.getUsername().isEmpty());
    boolean passwordEmpty = (credentials.getPassword()==null || credentials.getPassword().isEmpty());
    //getUserPassword return null if doesn't exist an user with username and password return null
    User user = getUserPassword(username,password);

    if (!usernameEmpty && !passwordEmpty && user!=null) {
        LOGGER.info("Found " + username);
    } else if (!usernameEmpty && !passwordEmpty && user==null) {
        LOGGER.info("There is no such username and password: " + username);
    } else if (usernameEmpty) {
        LOGGER.info("Username can not be empty ");
    } else if (passwordEmpty) {
        LOGGER.info("Password can not be empty ");
    }

    return user;
}

Ich kann mir zwei Optionen mit Vor- und Nachteilen vorstellen, um das Problem zu lösen.

Das erste besteht darin, Ausnahmen zu verwenden, aber ich denke, dass es keine gute Idee ist, andere Szenarien als erwartete wie Ausnahmen zu verwenden. Aus diesem Grund verwerfe ich es.

Das zweite ist das Einbeziehen des Objekts (Benutzers) in ein anderes Objekt, um die unterschiedlichen Möglichkeiten zu verwalten. Verwenden Sie beispielsweise Folgendes:

public class EntityObject<t> {
    //Is used to return the entity or entities if everything was fine
    private t entity;
    //Is used to inform of any checked exception
    private String exceptionMessage;

    //getters / setters / ..
}

public EntityObject<User> login(String username, String password) {
    boolean usernameEmpty = (credentials.getUsername()==null || credentials.getUsername().isEmpty());
    boolean passwordEmpty = (credentials.getPassword()==null || credentials.getPassword().isEmpty());
    User user = getUserPassword(username,password);
    EntityObject<User> entity = null;

    if (!usernameEmpty && !passwordEmpty && user!=null) {
        LOGGER.info("Found " + username);
        entity = new EntityObject<User>(user);
    } else if (!usernameEmpty && !passwordEmpty && user==null) {
        entity = new EntityObject<User>("There is no such username and password: " + username); 
    } else if (usernameEmpty) {
        entity = new EntityObject<User>("Username can not be empty ");
    } else if (passwordEmpty) {
        entity = new EntityObject<User>("Password can not be empty ");
    }

    return entity;
}

Ich mag diese zweite Option mehr als die erste, aber ich mag es nicht, dass ich die Methodensignatur ändern muss, um eine andere Klasse (EntityObject) als die übliche (User) zurückzugeben.

Was ist das übliche? Wie wird es normalerweise gehandhabt? Danke vielmals

3
FMCR 19 Apr. 2018 im 10:29

5 Antworten

Beste Antwort

Für mich sehen zweite Optionen besser aus. Um zu wissen, was der Fehler war, anstatt Nachrichten in Java-Code zu schreiben, können Sie wahrscheinlich enum mit möglichen Szenarien erstellen und im Front-End-Code beheben. Wenn Sie wirklich eine Nachricht benötigen, können Sie einen Konstruktor in enum erstellen um es zu speichern. Dies wird die Unterstützung vereinfachen und in Zukunft mit einem Objekt arbeiten. Das Hinzufügen weiterer Szenarien schadet Ihnen nicht viel.

Basisversion:

public class EntityObject<t> {
    //Is used to return the entity or entities if everything was fine
    private t entity;
    //Is used to inform of any checked exception
    private enum auth {
        NO_PASSWORD, NO_USERNAME, USER_DOES_NOT_EXIST, SUCCESS    
    }
}

Version mit enum Konstruktor:

public class EntityObject<t> {
    //Is used to return the entity or entities if everything was fine
    private t entity;
    //Is used to inform of any checked exception
    private enum auth {
        NO_PASSWORD("Password cannot be empty"),
        NO_USERNAME("Username cannot be empty"), 
        USER_OR_PASSWORD_DOES_NOT_EXIST("No such username or password exist"),
        SUCCESS("OK");
        public String message;
        public auth(String message) {
            this.message = message;
        }   
    }
}
1
Dmytro Chasovskyi 19 Apr. 2018 im 07:48

Eine Ausnahme sollte verwendet werden, wenn im System etwas Außergewöhnliches passiert. Für einen normalen Ablauf und etwas, das erwartet wird, sollten Sie Ausnahmen vermeiden.

Nach den guten SOLID-Prinzipien sollte Ihre Methode nur eines tun. Wenn es sich also um eine Methode handelt, um Benutzer anhand von Benutzername und Passwort zu finden, würde ich sagen, dass es am besten ist, null zurückzugeben. Der Grund ist nicht verloren. Eigentlich ist es ziemlich klar - es wird kein solcher Benutzer mit dem angegebenen Benutzernamen und Passwort gefunden (dieser Grund schließt das Problem mit dem leeren Benutzernamen ein und es ist der Fehler der Methode, einen leeren Benutzernamen für eine Anmeldemethode anzugeben). Durch Hinzufügen einer komplexen Logik zur Methode und zusätzlicher Entitäten für solche Dinge wird es schwieriger, Ihren Code zu pflegen und zu verstehen. Die Aufgabe dieser Methode besteht ohnehin nicht darin, die Validierung durchzuführen.

Wenn diese Klasse von einer Website oder einer API verwendet wird, können sie die Validierung durchführen (wenn Benutzername oder Kennwort leer sind).

3
Veselin Davidov 19 Apr. 2018 im 07:57

Ich würde sagen, dass der zweite Ansatz ziemlich gut ist. Wenn ich du wäre, würde ich das tun.

Wenn Sie den Rückgabewert wirklich nicht ändern möchten, können Sie eine weitere Methode hinzufügen, mit der überprüft wird, ob sich ein Benutzer anmelden kann:

public static final String SUCCESS = "Success"
public String checkLoginError(String username, String password) {
    // do all the checks and return the error message
    // return SUCCESS if no error
}

Jetzt kann die login Methode eine Zeile sein:

return getUserPassword(username,password);

Und Sie können es so verwenden:

String loginResult = checkLoginError(...);
if (loginResult.equals(SUCCESS)) {
    User loggedInUser = login(...)
} else {
    // do stuff with the error message stored in loginResult
}
1
Sweeper 19 Apr. 2018 im 07:49

Es scheint, dass Ihr Problem auf einer Methode beruht, die für mehrere Probleme verantwortlich ist.

Ich würde argumentieren, dass die login -Methode nicht prüfen sollte, ob diese Werte leer sind. Es gibt vermutlich eine Art Benutzeroberfläche (grafisch oder nicht), die einen Benutzernamen und ein Kennwort verwendet - dies sollte die Ebene sein, die die Validierung der Benutzereingaben durchführt.

Die login -Methode sollte sich nur damit befassen, ob die angegebenen Anmeldeinformationen mit einem Benutzer in Ihrem System übereinstimmen oder nicht. Es gibt nur zwei Ergebnisse - ja oder nein. Zu diesem Zweck können Sie Optional<User> verwenden. Es sollte tolerieren, dass die Zeichenfolgen leer sind, da dies ohnehin nie mit einem Benutzer übereinstimmt (vermutlich ist es für einen Benutzer unmöglich, in einem solchen Zustand zu existieren).

Hier ist ein Pseudocode:

void loginButtonPressed()
{
    if (usernameTextBox.text().isEmpty())
    {
        errorPanel.add("Username cannot be blank");
    }
    else if (passwordTextBox.text().isEmpty())
    {
        errorPanel.add("Password cannot be blank");
    }
    else
    {
        login(usernameTextBox.text(), passwordTextBox.text());
        // assign above result to a local variable and do something...
    }
}

public Optional<User> login(String username, String password)
{
    Optional<User> user = Optional.ofNullable(getUserPassword(username, password));
    user.ifPresentOrElse(
        user -> LOGGER.info("Found " + username),
        () -> LOGGER.info("Not found")
    );
    return user;
}
1
Michael 19 Apr. 2018 im 07:51

Die null -Werte von Java sind einer der schlimmsten Aspekte der Sprache, da Sie erst dann wirklich feststellen können, ob eine Methode einen Nullwert erhält, wenn dies geschieht. Wenn Sie eine IDE verwenden (ich hoffe es), können Sie überprüfen, ob sie steuern kann, ob Sie einen null -Wert übergeben, wo es keinen geben sollte (IntelliJ kann dies tun, indem Sie die Anmerkung @NotNull hinzufügen zu den Parametern der Methode).

Da dies gefährlich sein kann, ist es besser, das Weitergeben von null zu vermeiden, da dies sicherlich zu einem Fehler führen wird, sobald Ihr Code etwas komplexer wird.

Ich denke auch, dass es sinnvoll wäre, nur dann nach null Werten zu suchen, wenn eine konkrete Chance besteht, dass es einen geben könnte.

Wenn Sie ausdrücken möchten, dass ein Wert vorhanden sein kann oder nicht, verwenden Sie besser Optional<T>. Wenn aus irgendeinem Grund ein null -Wert anstelle eines realen Werts übergeben werden könnte, könnten Sie eine Dienstprogrammmethode erstellen, deren einziges Anliegen darin besteht, die Richtigkeit der Parameter zu überprüfen:

public Optional<EntityObject<User>> login(String username, String password) {
    //isNotNull shouldn't be necessary unless you can't validate your parameters
    //before passing them to the method.
    //If you can, it's not necessary to return an Optional
    if (isNotNull(username, password)) {
        //Since I don't know if a password must always be present or not 
        //I'm assuming that getUserPassword returns an Optional
        return Optional.of(new EntityObject<User>(getUserPassword(username,password).orElse(AN_EMPTY_USER)));
    } else {
        return Optional.Empty();
    }
}

Ich denke jedenfalls, dass das Validieren der Eingabe kein Problem der login -Methode sein sollte, selbst wenn Sie Optional nicht verwenden möchten. Es sollte stattdessen in einer anderen Methode durchgeführt werden.

1
gscaparrotti 19 Apr. 2018 im 11:33