2010-10-22 10 views
7

devo dire che enum ErrorCodes che"if" vs OO design

public enum ErrorCodes { 
     INVALID_LOGIN(100), 
     INVALID_PASSWORD(101), 
     SESSION_EXPIRED(102) ...; 

     private int errorCode; 

     private ErrorCodes(int error){ 
       this.errorCode = error; 
     } //setter and getter and other codes 
} 

ora ho controllare la mia codici di errore eccezione con questo codici di errore. Non voglio scrivere se questo fa questo, se questo fa questo. Come posso risolvere questo problema (scrivendo 10+ se blocchi)

C'è qualche motivo di progettazione per quella situazione?

Grazie

+0

È solo una "dichiarazione di caso" che aiuta o mi sfugge qualcosa? – Jean

+0

Sì, poiché in questo modo dovresti cambiare la tua implementazione ogni volta, l'enum cambia (nuove case statement ad esempio). Quando si implementa il codice basato su un buon design OO, dovresti provare ad essere aperto per le estensioni ma chiuso per le modifiche. – Mephisztoe

risposta

3

Come sottolineato da Spoike, l'utilizzo del polimorfismo per scegliere il giusto metodo di gestione degli errori è un'opzione. Questo approccio rimuove fondamentalmente i 10+ se blocchi alla ricerca del metodo virtuale della JVM, definendo una gerarchia di classi.

Ma prima di passare a una gerarchia di classi in piena regola, considerare anche l'utilizzo dei metodi enum. Questa opzione funziona bene se ciò che si intende fare in ciascun caso è abbastanza simile.

Ad esempio, se si vuole restituire un messaggio di errore diverso per ogni ErrorCode, si può semplicemente fare questo:

// Note singular name for enum 
public enum ErrorCode { 
    INVALID_LOGIN(100, "Your login is invalid"), 
    INVALID_PASSWORD(101, "Your password is invalid"), 
    SESSION_EXPIRED(102, "Your session has expired"); 

    private final int code; 
    private final String 

    private ErrorCode(int code, String message){ 
      this.code = code; 
      this.message = message; 
    } 

    public String getMessage() { 
     return message; 
    } 
} 

Poi il codice di gestione degli errori diventa solo:

ErrorCode errorCode = getErrorCode(); 
prompt(errorCode.getMessage()); 

Uno svantaggio di questo approccio è che se si desidera aggiungere ulteriori casi, è necessario modificare l'enum stesso, mentre con una gerarchia di classi è possibile aggiungere nuovi casi senza modificare il codice esistente.

+0

Grazie Ho risolto il mio problema aggiungendo questo metodo: public static String getErrorMessage (int errorCode); e trova il messaggio in ErrorCode.values. Grazie di tutto! –

+0

Buono a sapersi :) – oksayt

+0

A proposito, se si sta eseguendo il looping di tutti gli elementi ErrorCode.values ​​su ogni ricerca di codice di errore, controlla questa soluzione che può farlo in un tempo costante: http://stackoverflow.com/questions/ 2780129/get-enum-by-its-inner-field/2780170 # 2780170 – oksayt

2

Credo che il meglio che si possa fare sia implementare il modello di strategia. In questo modo non dovrai modificare le classi esistenti quando aggiungi nuove enumerazioni ma potrai comunque estenderle. (Aperto-chiuso-Principle).

Ricerca per schema di strategia e principio di apertura chiusa.

0

È possibile creare una mappa di codici di errore (intero) contro i tipi enum

Modifica

In questa soluzione, una volta che la mappa è preparato, è possibile cercare un codice di errore nella mappa e quindi non richiederà if..else lookup.

E.g.

Map<Integer, ErrorCodes> errorMap = new HashMap<Integer, ErrorCodes>(); 
for (ErrorCodes error : ErrorCodes.values()) { 
    errorMap.put(error.getCode(), error); 
} 

Ora, quando si desidera controllare un codice di errore proveniente dal vostro aplpication, tutto quello che dovete fare è,

ErrorCodes error = errorMap.get(erro_code_from_application); 

Così eliminando la necessità per tutto l'if..else.

È sufficiente impostare la mappa in modo che l'aggiunta di codici di errore non richieda modifiche nell'altro codice. La preparazione della mappa è un'attività di una volta e può essere collegata a un database, file di proprietà, ecc. Durante l'inizializzazione dell'applicazione

+0

Immagino che questa non sia la soluzione. Il mio codice è adesso ma non obbedisce ai principi. Voglio dire, scrivere se le istruzioni: if() else if() else if() .... else() non è una buona soluzione –

7

O lo si fa con un'istruzione if o uno switch oppure si implementa semplicemente la logica in questione in ErrorCode in qualche modo.

In modalità OO tutto dipende da come si desidera che l'applicazione o il sistema reagiscano al codice di errore. Diciamo che si desidera solo che l'uscita somekind di dialogo:

public doSomethingWithError() { 

    ErrorCodes e = getError(); 
    // the source of error, or originator, returns the enum 

    switch(e) { 
    case ErrorCodes.INVALID_LOGIN: 
     prompt('Invalid Login'); 
    case ErrorCodes.INVALID_PASSWORD: 
     prompt('Invalid password'); 
    // and so on 
    } 

} 

Potremmo invece creare una classe ErrorHandler che fa questo, invece:

// We'll implement this using OO instead 
public doSomethingWithError() { 

    ErrorHandler e = getError(); 
    // the originator now returns an ErrorHandler object instead 

    e.handleMessage(); 

} 

// We will need the following abstract class: 
public abstract class ErrorHandler { 

    // Lets say we have a prompter class that prompts the message 
    private Prompter prompter = new Prompter(); 

    public final void handleMessage() { 
    String message = this.getMessage(); 
    prompter.prompt(message); 
    } 

    // This needs to be implemented in subclasses because 
    // handleMessage() method is using it. 
    public abstract String getMessage(); 
} 

// And you'll have the following implementations, e.g. 
// for invalid logins: 
public final class InvalidLoginHandler() { 

    public final String getMessage() { 
    return "Invalid login"; 
    } 

} 

// E.g. for invalid password: 
public final class InvalidPasswordHandler() { 
    public final String getMessage() { 
    return "Invalid password"; 
    } 
} 

La prima soluzione è facile da implementare, ma diventa difficile mantieni il tempo in cui il codice diventa più grande. Quest'ultima soluzione è più complessa (alias Template Method pattern in seguito allo Open-Closed Principle) ma consente di aggiungere altri metodi allo ErrorHandler quando necessario (come il ripristino delle risorse o qualsiasi altra cosa). Puoi anche implementarlo con lo Strategy pattern.

Non si può ottenere completamente con le istruzioni condizionali, ma in quest'ultimo il condizionale viene spostato nella parte del codice in cui viene generato l'errore. In questo modo non avrai una doppia manutenzione sulle istruzioni condizionali sia all'originatore che al codice di gestione degli errori.

EDIT:

Vedi this answer by Michael Borgwardt e this answer by oksayt per come implementare metodi su Java Enums se si vuole fare questo, invece.

0

A mio parere non c'è niente di sbagliato in ErrorCodes come enumerazione e un'istruzione switch per la gestione degli errori di spedizione. Enums e switch si adattano perfettamente.

Tuttavia, è possibile trovare il seguente aspetto (tipo di over-design), vedere an Example o "Double dispatching" on Wikipedia. requisiti assume:

  • di gestione degli errori deve essere incapsulato in una classe propria
  • di gestione degli errori dovrebbe essere sostituibile
  • sicurezza
  • Tipo: Ogni volta che si aggiunge un errore, si è costretti ad aggiungere la gestione degli errori in ogni implementazione del gestore degli errori. Non è possibile "dimenticare" un errore in uno (di molti) cambi di posizione.

il codice:

//Inteface for type-safe error handler 
interface ErrorHandler { 
    void handleInvalidLoginError(InvalidLoginError error); 
    void handleInvalidPasswordError(InvalidLoginError error); 
//One method must be added for each kind error. No chance to "forget" one. 
} 

//The error hierachy 
public class AbstractError(Exception) { 
    private int code; 
    abstract public void handle(ErrorHandler); 
} 
public class InvalidLoginError(AbstractError) { 
    private String additionalStuff; 
    public void handle(ErrorHandler handler) { 
     handler.handleInvalidLoginError(this); 
    } 
    public String getAdditionalStuff(); 
} 
public class InvalidPasswordError(AbstractError) { 
    private int code; 
    public void handle(ErrorHandler handler) { 
     handler.handleInvalidPasswordError(this); 
    } 
} 

//Test class 
public class Test { 
    public void test() { 
     //Create an error handler instance. 
     ErrorHandler handler = new LoggingErrorHandler(); 

     try { 
      doSomething();//throws AbstractError 
     } 
     catch (AbstractError e) { 
      e.handle(handler); 
     } 
    } 
} 
4

enumerazioni Java sono molto potenti e permettono di metodo per ogni istanza implementazioni:

public enum ErrorCode { 
     INVALID_LOGIN { 
     public void handleError() { 
      // do something 
     } 
    }, 
     INVALID_PASSWORD { 
     public void handleError() { 
      // do something else 
     } 
    }, 
     SESSION_EXPIRED { 
     public void handleError() { 
      // do something else again 
     } 
    }; 

    public abstract void handleError(); 
} 

allora si può semplicemente chiamare errorCode.handleError();. Tuttavia, è discutibile se un enum ErrorCode sia davvero il posto giusto per quella logica.

+1

Ah, non mi rendevo conto che potresti aggiungere metodi astratti in un enum. Abbastanza intelligente. – Spoike

+0

Grazie in anticipo. Penso di implementare un'interfaccia che estenda l'altra interfaccia a troppa codifica. Cancella tutto. Questo approccio è molto interessante –