Evitare null in un controller

2

Sto cercando di capire come scrivere questo codice.

def get(params):
    """ Fetch a user's details, or 404 """
    user = User.fetch_by_id(params['id'])
    if not user:
        abort(404)
    # Render some template for the user...

Qual è il modo migliore per gestire il caso in cui la ricerca fallisce? Un principio dice che dovresti evitare di restituire valori nulli dalle funzioni. Questi portano a errori e AttributeErrors ecc. Più avanti nel file.

Un'altra idea è di avere fetch_by_id di generare un errore ValueError o simile se nessun utente esiste con quell'id. Tuttavia, esiste un principio generale che non deve utilizzare eccezioni per il controllo flusso , che non aiuta molto.

Cosa si potrebbe fare meglio in questo caso?

    
posta Kevin Burke 26.06.2013 - 06:02
fonte

3 risposte

2

Non sarò troppo contrario al primo approccio (restituendo null ), ma rinominerò il metodo per indicare chiaramente che potrebbe restituire null.

Firme come:

findCustomer(int customerId)
parse(string number)
pop(stack sequence)

non sono chiari. Se un cliente non esiste, il metodo restituisce null o genera un'eccezione CustomerNotFound ? Se il numero è "abc" e non può essere ragionevolmente analizzato, il metodo restituisce null o l'eccezione InvalidFormat di lancio? Cosa succede quando pop ing in un elemento da uno stack vuoto?

Invece:

findCustomerIfAny(int customerId)
tryParse(string number)
lastOrNull(stack sequence)

sono più facili da capire.

Altre opzioni che hai incluso:

1. out parametri

Se la tua lingua supporta i parametri out , un approccio diverso dovrebbe essere il seguente:

tryFindCustomer(int customerId, out customer result) returns bool

Il risultato booleano indica se il cliente è stato trovato o meno.

Personalmente, trovo questo approccio troppo prolisso e brutto. Non lo userò.

2. Tuple

Se la tua lingua ha delle tuple, puoi scrivere:

findCustomer(int customerId) returns (bool isFound, customer match)

Questo è perfettamente valido e potrebbe rendere il tuo codice estremamente esplicito.

3. Valore o impostazione predefinita

C'è anche un'altra interessante convenzione in LINQ in .NET. Un metodo che cerca il primo elemento di una sequenza, FirstOrDefault , restituisce il valore predefinito se la sequenza è vuota.

Questo significa che:

[ 2, 7, 1 ].FirstOrDefault()

fornisce 2 , mentre:

[].FirstOrDefault()

indica 0 , poiché default(int) è 0 . Nota che:

default(customer) ↔ null
default(System.Drawing.Point) ↔ System.Drawing.Point.Empty ↔ (0, 0)

ecc.

Purtroppo, non puoi eseguire l'override dell'operatore default , il che rende questo approccio piuttosto limitato.

Non sono sicuro che questo ultimo approccio ti aiuterà nel tuo caso, ma vale la pena menzionarlo.

4. Stato all'interno del valore restituito

Un approccio personalizzato sarebbe quello di integrare un valore di stato all'interno della classe stessa. Ad esempio:

// 'findCustomer' always returns a non-null value.
selectedCustomer = findCustomer(customerIdFromInput)

// When the customer cannot be found, 'IsEmpty' is set to 'true'.
if (selectedCustomer.IsEmpty) {
    throw CustomerNotFoundException;
}
    
risposta data 26.06.2013 - 06:54
fonte
1

Rinominare i metodi come suggerito da MainMa è una possibilità, anche se i nomi dei metodi risultanti potrebbero non essere interessanti per tutti. Nella riga della domanda su come evitare effettivamente di restituire null , ecco altre due opzioni:

Opzione / Forse

Diverse lingue supportano direttamente un tipo che codifica se hai trovato o meno un oggetto o meno (ad esempio Option in Scala o Maybe in Haskell). Con questi, in pratica, avvolgi i tuoi risultati all'interno di un altro tipo per assicurarti di fornire un risultato non nullo. Più precisamente, se hai trovato l'oggetto user , non puoi t return utente but Alcuni (utente) , where Alcuni is an Opzione . If you did not find the user, you instead return a Nessuno object, which is better than a null , as you at least have the Opzione 'interfaccia disponibile su di esso .

Per le lingue che non supportano direttamente queste, in genere ci sono librerie aggiuntive che puoi inserire (ad esempio Optional in guava per Java ).

Motivo oggetto nullo

Un'opzione diversa è il modello di oggetto nullo . In sostanza, significa che devi restituire un oggetto Utente, indipendentemente da ciò che accade. Nel caso in cui non si riesca a trovare un oggetto utente per il proprio ID, è necessario restituire un oggetto null utente appositamente predisposto. Il vantaggio di questo oggetto è che implementa l'interfaccia completa di una classe User ei chiamanti non devono preoccuparsi se l'utente è stato trovato o meno (potrebbe volerlo comunque, ed è per questo che ha senso aggiungere qualche metodo isNull o tale all'interfaccia).

Di ', vuoi scrivere un codice che ha dato un ID utente in qualche modo alle email di quell'utente riguardo alla tua nuova così bella promozione. Con il modello di oggetto null è possibile recuperare l'utente tramite id, ma invece ottenere l'oggetto null-user. Su quello puoi semplicemente chiamare un metodo getEMailAddress o simile, che potrebbe restituirti un indirizzo di fallback come [email protected] o qualsiasi altra cosa che ti si addice. Il tuo codice completo non ha bisogno di verificare se esiste un utente con quell'id.

    
risposta data 26.06.2013 - 07:15
fonte
0

I'm trying to work through how to write this code.

def get(params):
  """ Fetch a user's details, or 404 """
  user = User.fetch_by_id(params['id'])
  if not user:
    abort(404)
  # Render some template for the user...

What's the best way to handle the case where the lookup fails? One principle says you should avoid returning null values from functions.

Eh ... IMO ci sono solo due opzioni ragionevoli per una ricerca diretta come "find_by_id". Tutto il resto è eccessivamente complicato.

1) return something false-y, like null
2) raise an exception

Restituire null è leggermente più semplice quando il chiamante verificherà immediatamente il valore restituito. Lanciare un'eccezione è più semplice quando il chiamante vuole eseguire più azioni e gestire gli errori in modo identico.

IMO un'API generica dovrebbe fornire entrambi per il recupero di record singoli, ad es. 'find_by_id', che può restituire null, e 'fetch_by_id' che restituisce un record o solleva un'eccezione.

    
risposta data 26.06.2013 - 09:44
fonte

Leggi altre domande sui tag