Interroga qualcosa e restituisce il motivo se non è stato trovato nulla

5

Supponiamo di avere una query - come in CQS che dovrebbe restituire un singolo valore .

Supponiamo che il caso in cui non venga trovato alcun valore non è eccezionale, quindi in questo caso non verrà generata alcuna eccezione. Invece, viene restituito null .

Tuttavia, se non è stato trovato alcun valore, devo agire in base al motivo perché non è stato trovato alcun valore.

Supponendo che la Query ne conosca il motivo, come potrei comunicarlo al chiamante della Query?

Una soluzione semplice non sarebbe restituire direttamente il valore ma un oggetto contenitore che contiene il valore e il motivo:

public class QueryResult
{
    public TValue Value { get; private set; }
    public TReason ReasonForNoValue { get; private set; }
}

Ma questo sembra goffo, perché se viene trovato un valore, ReasonForNoValue non ha senso e se non è stato trovato alcun valore, Value non ha senso.

Quali altre opzioni devo comunicare il motivo? Cosa ne pensi di un evento per ragione?

Per riferimento: questo sarà implementato in C #.

    
posta Daniel Hilgarth 25.06.2013 - 15:52
fonte

6 risposte

14

Molti protocolli Internet sono costruiti attorno a un codice di risposta che viene sempre restituito insieme a un messaggio associato. SMTP e HTTP essendo due esempi ben noti.

Quindi, public TReason ReasonForNoValue diventa qualcosa di più lungo le linee di public TResponse ResponseCode

La risposta potrebbe essere un numero intero e seguire l'esempio di stile SMTP e HTTP, o potrebbe essere un enum per alcuni tipi di sicurezza, o anche una stringa (anche se questo impone alcuni pericoli di non fare confronti tra stringhe correttamente da qualche parte, o un refuso ).

Quando c'è un errore (e indicato dal ResponseCode), il valore potrebbe quindi contenere informazioni più specifiche (simili a una pagina 404 in HTTP).

    
risposta data 25.06.2013 - 16:59
fonte
3

Mi piace la soluzione che proponi. Potresti in seguito desiderare di refactificare ReasonForNoValue in qualcosa di più generico, ma almeno hai la struttura per farlo se necessario.

Vantaggi del tuo approccio:

  • È espandibile se è necessario restituire più dati o metadati
  • Separa i dati effettivi dai metadati
  • Non ci sono magic numbers
  • È facile da analizzare.

Telastyn ha suggerito un altro buon approccio: è sufficiente restituire un singolo TValue e supportare una nuova chiamata al server WhyWasThisNull()

    
risposta data 25.06.2013 - 16:33
fonte
1

Prima di tutto, molto probabilmente userò un'eccezione in questo caso. Slogan accattivanti a parte, le eccezioni sono il modo standard per specificare in modo conciso un flusso di controllo "di successo" e un flusso di controllo ortogonale "non riuscito". Quanto spesso il percorso di non successo ha poco a che fare con esso, specialmente quando si aggiunge l'overhead di eccezioni al sovraccarico di query del database molto più grande.

La mia seconda scelta sarebbe che la classe chiamante implementasse un'interfaccia di risultato, qualcosa del genere:

public class Caller implements ResultHandler {

    public void findValue(Query query) {
        query.execute(this);
    }

    public void valueFound(Value value) {
        System.out.println("Found value " + value);
    }    

    public void valueNotFound(String reason) {
        System.err.println("Value not found: " + reason);
    }
}

Come un'eccezione, questa soluzione ha il vantaggio di non essere identificabile su valori nulli. Questa architettura semplifica anche altre operazioni mentre aspetti che la query venga completata. Inoltre, dovresti davvero inserire valueFound e valueNotFound nelle loro funzioni in ogni caso.

Il tuo oggetto QueryResult è migliore del restituire null , ma ha lo svantaggio di richiedere il codice di codice di riscaldamento ovunque per controllare il codice di risposta. Se in seguito qualcuno dimenticherà quell'alfabeto, o lo ometterà intenzionalmente pensando che il valore sarà sempre trovato, non si può sapere quale stato inserirà il codice.

In altre parole, aggiunge onere ai manutentori. Può sembrare palesemente ovvio che devi controllare il codice di risposta ora, ma non sarà così scontato tra due anni. Più errori vengono catturati dal compilatore, meglio è.

    
risposta data 26.06.2013 - 04:43
fonte
0

What other options do I have to communicate the reason?

Se il risultato è una stringa, puoi avere il motivo esserci con una firma che indica che si tratta di un risultato non valido.

Se il tuo risultato è un numero, puoi avere un negativo (o altri valori errati) come un codice di errore.

Puoi avere una query separata che gli utenti possono chiamare (se lo desiderano) per determinare la causa di null.

Nessuno di questi sembra migliore del tuo oggetto risultato / errore, tranne forse l'ultimo a seconda dell'utilizzo.

What do you think of one event per reason?

Penso che gli eventi siano un modo in gran parte terribile di dispiegare la logica e un modo abbastanza rigido / accoppiato di segnalare un errore di interrogazione che dipende specificamente dalla memoria / formato dei dati e dovrebbe cambiare se la query cambia per causare diversi tipi di fallimento.

    
risposta data 25.06.2013 - 16:28
fonte
0

Potresti usare il tuo oggetto e fare qualcosa del tipo:

public class QueryResult
{
    public TValue Value { get; private set; }
    public TQuerySuccess SuccesValue { get; private set; }
}

Leggi il valore di successo per vedere se la query ha avuto successo, se lo fosse, il valore conterrà queryresult, se non fosse il valore conterrà il failreason.

    
risposta data 25.06.2013 - 16:58
fonte
0

Il mio suggerimento è fare uno di questi:

1. Usa l'oggetto QueryResult ma cambia il nome di QueryResult.ReasonForNoValue () in QueryResult.Message () .

public class QueryResult
{
    public TValue Value { get; private set; }
    public TMessage Message { get; private set; }
}

2. Rise KeyUnknowException o NoTranslationException , anche se hai già detto che questa soluzione non ti piace.

3. Restituisce sempre un valore non nullo . Esistono solo due valori di stringa speciali: "#UKNOWN_KEY" e "#NO_TRANSLATION".

EDIT: (grazie a @DanielHilgarth) Dovresti documentare diverse cose:

  1. Che un valore mancante non restituisce null , ma ancora string .
  2. Che un valore mancante è indicato da diverse stringhe magiche (con le stesse implicazioni delle costanti magiche ).
  3. Cosa significa ciascuna di quelle stringhe magiche.

E alla fine qualcuno dovrebbe effettivamente leggere quella documentazione.
In generale, questo tipo di architettura aumenta la possibilità di bug.

    
risposta data 25.06.2013 - 16:48
fonte

Leggi altre domande sui tag