È una cattiva pratica di codifica creare qualcosa in un get se non esiste?


18

Quindi ho un servizio web che ha qualcosa come un getAccount cui restituirebbe un identificatore all'account se lo avesse ottenuto, altrimenti genera un'eccezione. Il cliente vorrà sempre creare un account se viene generata un'eccezione con le stesse informazioni con cui si ottiene get.

Sto creando una libreria di convenienza per i clienti che gestiranno tutte le chiamate al servizio web all'interno in modo che non debbano sapere come effettuare le chiamate da soli.

Quello che mi chiedo è in questa libreria se dovessi creare un getAccount(accountName)account che ottenga l'account se esiste, e se non lo crea e restituisce le informazioni, è una cosa negativa da fare? Devo lasciarlo al client per gestire le eccezioni o semplicemente chiamarlo come getOrCreateAccount? Importa?

È una cattiva pratica creare qualcosa in un'operazione get?


9
Per lo meno, lo nominerei getOrCreateAccounto simile.
Telastyn,

4
Sembra valido nel contesto dell'inizializzazione lazy. Tuttavia, dipende se hai bisogno di usare quel modello nella tua libreria.
Chris C,

1
Mi piace il verbo acquire, mi piace acquireAccount. Non ha un significato esistente in nessuno dei principali protocolli che ho incontrato e ha un anello imperativo che gli si adatta bene. "Fai tutto quello che devi fare per acquisirne uno. Richiedilo, costruiscilo, fingilo, rubalo, non mi interessa, fammene uno o muori provandoci."
Dan Ross,

2
"Il cliente vorrà sempre creare un account" - sembra molto insolito. Se non riesco a ottenere l'account perché l'utente ha sbagliato a digitare il proprio nome utente, certamente non voglio creare un account con il nome errato.
gnasher729,

Secondo la specifica javabean oracle.com/technetwork/articles/javaee/spec-136004.html getSomething() è per i getter ed setSomething()è per i setter. Imo tutto ciò che fa qualcosa di più intellettuale deve essere chiamato qualcos'altro, cioè fetchSomething, obtainSomething, computeSomething, o doSomethingElseecc
ccpizza

Risposte:


31

Sì, è importante. Secondo me, è generalmente una cattiva pratica creare qualcosa in una procedura che non è documentata come dotata dei poteri della creazione. Denominare la procedura getOrCreate...o disporre di una create...procedura separata e quindi, se proprio lo si desidera, eseguire getOrCreate...prima un tentativo get...e, in caso contrario, chiamare create...e quindi chiamare get....

Probabilmente l'utente della libreria non si aspetta che la get...procedura venga creata se l'operazione get non riesce. Se improvvisamente scoprono che il loro test chiama per get...creare un'intera tonnellata di dati, probabilmente saranno piuttosto sorpresi. E come lo puliscono? Che cosa succede se scrivono codice pensando che otterranno un errore se get...falliscono e vogliono gestirlo a modo loro ?


Grazie, la creazione in realtà restituisce l'ID che restituirebbe anche get, quindi non dovrei fare get... create... get...solo i primi due. Parlerò al cliente se avranno mai bisogno della possibilità di chiamare semplicemente un getsenza voler creare
Mike

6
@Mike: penso ancora che dovrebbe avere createil nome, giusto per essere al 100% chiaro su ciò che sta accadendo.
FrustratedWithFormsDesigner

Sì, sono d'accordo, stavo pianificando di renderlo qualcosa sulla falsariga di getOrCreate perché il mio pensiero originale era solo quello di ottenere, ma mi sono reso conto che non è immediatamente chiaro che sta anche creando qualcosa in background
Mike,

1
È super in ritardo ma getOrCreateha la precedenza in un popolare framework Web: docs.djangoproject.com/en/1.10/ref/models/querysets/…
tex

18

No, non è una "cattiva pratica". Fintanto che tu e gli altri sviluppatori siete d'accordo sul fatto che si desidera che funzioni, va bene. Dopotutto, sarebbe restituire un account, che è quello che vuoi. Il fatto che l'account venga creato "sottocoperta" è irrilevante per il chiamante.


10
L'unica eccezione è se si tratta di un'API pubblicamente disponibile. La comunità più ampia ha già concordato che GET è indempotente e nullipotent; in altre parole, la stessa azione viene eseguita ogni volta ed è un metodo sicuro che non modifica lo stato del server. Questo è nel Wiki REST . A parte questo, se l'API esiste solo nel tuo piccolo mondo, fai tutto ciò che è accettato dal tuo gruppo.
jmort253,

9
Non sono d'accordo, anche per un'API pubblica va bene, purché abbia senso nel contesto di qualunque cosa l'API dovrebbe fare. È inutile creare più voci nell'API quando una sola farebbe il trucco.
GrandmasterB,

Per la cronaca, si tratta di una libreria interamente interna e ho la possibilità di dire al team di usarlo come funziona
Mike

1
@ jmort253: un servizio è nullipotent se non presenta effetti collaterali visibili al client . Il server può fare come gli pare.
Kevin Cline,

1
@kevincline, immagino che ci stavo pensando in termini, diciamo, di addebito sulla mia carta di credito. Se il server addebita la mia carta sulla base di una richiesta GET ma mi dà comunque un risultato come "rifiutato" ogni volta che lo eseguo, sembra che vada contro lo spirito di GET. Detto questo, vedo il tuo punto. Il server potrebbe contare il numero di richieste GET e bloccarmi dopo 3 query, limitandomi in tal modo a limitarmi, ma senza modificare i dettagli del mio account. Penso che sia una distinzione importante quando si dice che lo stato del server non viene modificato. Forse dovrei dire "stato client sul server" invece
jmort253

10

Se getAccount()è sempre possibile restituire un account, quindi dal punto di vista del chiamante, l'account esiste ed è sempre esistito. Non è necessario per getAccount()"creare" nulla. L'account non deve essere archiviato in alcun luogo finché non è diverso dall'account predefinito.


+1 In genere, GetOrCreateè il semantico sbagliato ma ottenere un oggetto che può "logicamente" esistere indipendentemente dal fatto che esista o meno dal punto di vista fitologico va bene. Ad esempio, una matrice sparsa di elementi mutabili potrebbe non disporre di alcuna memoria allocata per l'elemento 1.841.533, ma consentire comunque a tale elemento di essere "recuperato" creando un nuovo oggetto, memorizzandolo e restituendo un riferimento.
supercat,

4

Ha più senso creare 3 metodi:

getAccount -> Che ottiene solo l'account.

createAccount -> Crea un account.

getAccountAndCreateIfNeeded -> Scegli il tuo nome;)

Perché la separazione: hai un metodo semplice per ottenere e creare. Questo è un chiaro metodo verificabile per entrambi. Per getAccount non è un'eccezione non trovare l'account. Quindi, basta restituire false o qualcosa del genere, è previsto.

Quindi è possibile utilizzare quel valore restituito nella funzione raggruppata: getAccountAndCreateIfNeeded che ora è anche testabile, dovrebbe restituire sempre un account. Qualunque cosa tu chieda.

Tutti questi 3 metodi sono chiari, è esattamente chiaro cosa fanno e cosa restituiscono. Puoi concludere accordi con la tua squadra, ma questo tipo di eccezioni è terribile a lungo termine. Rendili chiari e non avrai problemi.


Inoltre, da Clean Code: "Se il tuo metodo non può fare ciò che implica il suo nome, lancia un'eccezione". Avrei un blocco Try / Catch all'interno di 'GetOrCreateIfneeded' che genera un GET fallito e quindi ha 'createAccount' all'interno del Throw per qualunque tipo di eccezione ritorni per il get fallito.
Graham,

Potrei suggerire un quarto metodo:, getAccountIfExistsche potrebbe ottenere un account o indicare che non esiste senza crearne uno nuovo. Il getAccountmetodo stesso dovrebbe presupporre l'esistenza dell'account e, in caso contrario, generare un'eccezione.
supercat

2

Dipende dalle circostanze.

Ad esempio, è possibile utilizzarlo per eseguire il caricamento / istanza lazy, rinviando il caricamento dei dati o la creazione di un'istanza fino a quando non è effettivamente necessario. Questo è normalmente ragionevole in quanto consente di risparmiare risorse che potrebbero non essere necessarie (se la classe / i dati non sono mai necessari, non vengono mai caricati).

Tuttavia, in questo caso particolare, direi che avere un metodo chiamato getAccount che creerebbe un nuovo account se non esistesse non sarebbe una buona pratica. Se l'utente ha fornito alcune credenziali per identificare un determinato account e tale account non è stato trovato, significa che l'utente non è ancora un cliente e dovrebbe avere un account creato per loro o significa che le credenziali non sono state digitate correttamente e all'utente deve essere chiesto di verificare di aver inserito ciò che intendeva inserire?

Se disponi di un metodo getAccount che crea un nuovo account se non è in grado di identificarne uno, non hai scelta. Se dividi la creazione e l'output dell'account in metodi separati, allora hai molta più flessibilità nel decidere cosa fare se un tentativo di ottenere un account fallisce.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.