Verifica la validità dei dati in ogni costruttore, o ritieni che i dati siano corretti e crei delle eccezioni nella funzione specifica che ha un problema con il parametro?Dovresti controllare i valori errati dei parametri nel costruttore?
risposta
Un costruttore è anche una funzione: perché differenziarsi?
La creazione di un oggetto implica che siano stati eseguiti tutti i controlli di integrità. È perfettamente ragionevole controllare i parametri in un costruttore e lanciare un'eccezione una volta che è stato rilevato un valore illegale.
Tra tutto ciò semplifica il debug. Quando il programma genera un'eccezione in un costruttore, è possibile osservare una traccia dello stack e spesso vedere immediatamente la causa. Se si ritarda il controllo, è necessario eseguire ulteriori indagini per rilevare quale evento precedente causa l'errore corrente.
È sempre meglio avere un oggetto completamente costruito con tutti gli invarianti "soddisfatti" fin dall'inizio. Attenzione, tuttavia, a lanciare eccezioni dal costruttore in lingue non gestite poiché ciò potrebbe causare una perdita di memoria.
Se si inserisce il costruttore, è più probabile che la traccia dello stack mostri da dove provengono i valori errati.
Sono d'accordo con sharptooth nel caso generale, ma a volte ci sono oggetti che possono avere stati in cui alcune funzioni sono valide e altre no. In queste situazioni è meglio rinviare i controlli alle funzioni con queste particolari dipendenze.
In genere è consigliabile eseguire la convalida in un costruttore, se non altro perché ciò significa che gli oggetti saranno sempre in uno stato valido. Ma alcuni tipi di oggetti richiedono controlli specifici per funzione, e va bene anche questo.
Ho sempre forzato i valori nei costruttori. Se gli utenti non possono essere disturbati a seguire le regole, li applico silenziosamente senza dirglielo.
Quindi, se passano un valore del 107%, lo imposterò al 100%. Devo solo chiarire nella documentazione che è quello che succede.
Solo se non c'è una coercizione logica evidente, restituisco loro un'eccezione. Mi piace definirlo il "principale del più stupore per quelli troppo pigri o stupidi per leggere la documentazione".
sembra interessante, ma questo non porta a un codice che si comporta inaspettatamente alcune volte? – Homes2001
Sì, ma solo a quelli che non hanno letto il doco :-) – paxdiablo
uhm ... Penso che questo possa portare a un codice difficile da usare/capire: ci sono molti casi in cui avrei difficoltà a individuare il "evidente coercizione logica" –
Questa è una domanda difficile. Ho cambiato la mia abitudine legati a questa domanda più volte negli ultimi anni, per cui qui sono alcuni pensieri che vengono in mente:
- Verifica gli argomenti nel costruttore è come controllare gli argomenti a favore di un metodo tradizionale ... quindi non vorrei differenziare qui ...
- Controllare gli argomenti di un metodo ovviamente aiuta a garantire che i parametri passati siano corretti, ma introduce anche molto più codice che devi scrivere, il che non sempre ripaga secondo me ... Soprattutto quando si scrivono metodi brevi che delegano abbastanza frequentemente ad altri metodi, immagino che si finirebbe con il 50% del codice solo facendo controlli sui parametri ...
- Furthermor Considera che molto spesso quando scrivi un test unitario per la tua classe, non vuoi dover passare dei parametri validi per tutti i metodi ... Molto spesso ha senso passare solo quei parametri importanti per il caso di test corrente , quindi fare controlli ti rallenterebbe nello scrivere i test unitari ...
Penso che questo dipenda davvero dalla situazione ...quello che ho finito è scrivere solo i controlli dei parametri quando so che i parametri provengono da un sistema esterno (come input dell'utente, database, servizi web o qualcosa del genere ...). Se i dati provengono dal mio stesso sistema, non scrivo i test per la maggior parte del tempo.
Circa il tuo terzo punto: perché farebbe piacere testare qualcosa che non accadrà mai? Se si controllano i parametri nel costruttore, i valori non saranno mai il valore errato. Quindi non devi testare lì. Verifica solo il costruttore con parametri errati. –
In teoria, il codice di chiamata deve sempre garantire che vengano soddisfatte le condizioni preliminari, prima di chiamare una funzione. Lo stesso vale per i costruttori.
In pratica, i programmatori sono pigri, e per essere sicuri è meglio controllare ancora le condizioni preliminari. Gli asserti ci tornano utili.
Esempio. Scusa la sintassi del controvento non ricurvo:
// precondition b<>0
function divide(a,b:double):double;
begin
assert(b<>0); // in case of a programming error.
result := a/b;
end;
// calling code should be:
if foo<>0 then
bar := divide(1,0)
else
// do whatever you need to do when foo equals 0
In alternativa, è sempre possibile modificare le condizioni preliminari. Nel caso di un costruttore questo non è molto utile.
// no preconditions.. still need to check the result
function divide(a,b:double; out hasResult:boolean):double;
begin
hasResult := b<>0;
if not hasResult then
Exit;
result := a/b;
end;
// calling code:
bar := divide(1,0,result);
if not result then
// do whatever you need to do when the division failed
Aspetta un minuto, non è solo C senza parentesi graffe. È Pascal! Stai attento ragazzi, ha un compilatore Pascal. Correre per la vostra vita. – paxdiablo
Fallire sempre il prima possibile. Un buon esempio di questa pratica è esposto dal runtime. * se si tenta di utilizzare un indice non valido per un array si ottiene un'eccezione. * la trasmissione fallisce immediatamente quando non viene tentata in una fase successiva.
Immaginate cosa sarebbe un codice di disastro se un cast non valido è rimasto in un riferimento e ogni volta che provate a usarlo avete ottenuto un'eccezione. L'unico approccio ragionevole è quello di fallire il prima possibile e cercare di evitare uno stato non valido/non valido al più presto.
Questo è lo stesso. Vero che non tutti i metodi possono essere richiamati legalmente in qualsiasi stato dell'oggetto. Inoltre, puoi spesso identificare le combinazioni di valori dei parametri che possono essere raggruppati in costruttore e generare eccezioni se si presentano altre combinazioni. – sharptooth
Inoltre, se il tuo oggetto ha dei metodi che sono sempre validi e altri no, potresti considerare se il tuo oggetto è troppo grande. Forse dovrebbe essere diviso? – sleske