Stile di codice buono per introdurre controlli dei dati ovunque?

10

Ho un progetto di dimensioni sufficientemente grandi che non riesco più a mantenere ogni aspetto nella mia testa. Ho a che fare con un certo numero di classi e funzioni e sto passando i dati.

Con il tempo ho notato che continuavo a ricevere errori, perché ho dimenticato quale forma precisa i dati devono avere quando li passo a funzioni diverse ( ad esempio, una funzione accetta e genera una serie di stringhe, un'altra funzione, che ho scritto molto più tardi, accetta stringhe che sono conservate in un dizionario ecc., quindi devo trasformare le stringhe con cui sto lavorando da averle in un array per averli in un dizionario ).

Per evitare di dover sempre capire cosa ha rotto dove, ho iniziato a trattare ogni funzione e classe come una "entità isolata" nel senso che non può fare affidamento su codice esterno dandogli l'input corretto e deve eseguire controlli di input (o, in alcuni casi, rifare i dati, se i dati sono dati nella forma sbagliata).

Ciò ha notevolmente ridotto il tempo che trascorro facendo in modo che i dati che ho trasmesso vadano "adattati" a tutte le funzioni, perché le classi e le funzioni stesse ora mi avvertono quando alcuni input sono errati (e talvolta anche corretto) e io non devi più utilizzare un debugger attraverso l'intero codice per capire dove qualcosa è andato in tilt.

D'altra parte questo ha anche aumentato il codice complessivo.
La mia domanda è, se questo stile di codice è appropriato per risolvere questo problema?
Naturalmente, il migliore la soluzione sarebbe quella di ridefinire completamente il progetto e assicurarsi che i dati abbiano una struttura uniforme per tutte le funzioni, ma dal momento che questo progetto è in costante crescita, finirei a spendere di più e mi preoccupo del codice pulito che aggiungere nuove cose.

(FYI: Sono ancora un principiante, quindi scusami se questa domanda è ingenua, il mio progetto è in Python.)

    
posta user7088941 20.04.2018 - 09:53
fonte

4 risposte

4

Una soluzione migliore è sfruttare maggiormente le funzionalità e gli strumenti del linguaggio Python.

For example, in function 1, the expected input is an array of strings, where the first string denotes the title of something and the second a bibliographical reference. In function 2 the expected input is still an array of strings, but now the roles of the strings are inversed.

Questo problema viene mitigato con un namedtuple . È leggero e offre un facile significato semantico ai membri dell'array.

Per sfruttare i vantaggi di alcuni tipi di controllo automatico dei caratteri senza passare da una lingua all'altra, puoi sfruttare il tipo di suggerimento . Un buon IDE può usarlo per farti sapere quando fai qualcosa di stupido.

Sembra anche preoccupato che le funzioni diventino obsolete quando cambiano i requisiti. Questo può essere rilevato da test automatici .

Anche se non sto dicendo che il controllo manuale non è mai appropriato, un uso migliore delle funzionalità linguistiche disponibili può aiutarti a risolvere questo problema in modo più gestibile.

    
risposta data 20.04.2018 - 13:59
fonte
9

OK, il problema reale è descritto in un commento sotto questa risposta:

For example, in function 1, the expected input is an array of strings, where the first string denotes the title of something and the second a bibliographical reference. In function 2 the expected input is still an array of strings, but now the roles of the strings are inversed

Il problema qui è l'uso di un elenco di stringhe in cui l'ordine significa semantica. Questo è un approccio molto incline agli errori. Invece dovresti creare una classe personalizzata con due campi chiamati title e bibliographical_reference . In questo modo non hai intenzione di mescolarli e tu eviterai questo problema in futuro. Ovviamente questo richiede un refactoring se usi già elenchi di stringhe in molti posti, ma credimi, a lungo andare sarà più economico.

L'approccio comune in tipi di linguaggi dinamici è "digitazione anatra", il che significa che non ti interessa molto del "tipo" dell'oggetto passato, ti interessa solo se supporta i metodi che stai invocando. Nel tuo caso, ti basterà leggere il campo chiamato bibliographical_reference quando ne hai bisogno. Se questo campo non esiste sull'oggetto passato, si otterrà un errore e questo indica che il tipo errato è passato alla funzione. Questo è un buon tipo di controllo come qualsiasi.

    
risposta data 20.04.2018 - 11:05
fonte
3

Prima di tutto, ciò che stai sperimentando in questo momento è codice odore - prova a ricorda cosa ti ha portato a diventare consapevole dell'odore e cerca di affinare il tuo naso "mentale", poiché prima noterai un odore di codice prima - e più facile - sarai in grado di risolvere il problema sottostante.

To avoid always having to figure out what broke where, I started treating each function and class as being an "isolated entity" in the sense that it cannot rely on outside code giving it the correct input and has to perform input checks itself.

La programmazione difensiva, come viene chiamata questa tecnica, è uno strumento valido e spesso utilizzato. Tuttavia, come con tutte le cose, è importante usare la giusta quantità, troppi pochi controlli e non si incontreranno problemi, troppi e il tuo codice sarà troppo gonfio.

(or, in some cases, recast the data, if the data is given in the wrong form).

Potrebbe essere un'idea meno buona. Se noti che una parte del tuo programma chiama una funzione con dati formattati in modo errato, FISSARE QUESTA PARTE , non modificare la funzione chiamata per poter comunque digerire i cattivi dati.

This has greatly reduced the time I spend making sure that the data that I pass around "fits" into every function, because the classes and functions themselves now warn me when some input is bad (and sometimes even correct that) and I don't have to go with a debugger through the whole code anymore to figure out where something went haywire.

Migliorare la qualità e l'amp; la manutenibilità del tuo codice è un risparmio di tempo nel lungo periodo (in questo senso devo ancora mettere in guardia contro la funzionalità di autocorrezione che hai incorporato in alcune delle tue funzioni - potrebbero essere una fonte insidiosa di bug. Solo perché il tuo programma non si blocca & burn non significa che funzioni correttamente ...)

Per rispondere finalmente alla tua domanda: Sì, la programmazione difensiva (cioè la verifica della validità dei parametri forniti) è - in buona salute - una buona strategia. Ciò detto , come hai detto tu stesso, il tuo codice è inconsistente e io strongmente ti consiglio di dedicare un po 'di tempo al refactoring delle parti che odorano - hai detto che non vuoi preoccuparti del codice pulito tutto il tempo, dedica più tempo alla "pulizia" che alle nuove funzionalità ... Se non mantieni il tuo codice pulito, potresti passare il doppio del tempo "salva" da non mantenere un codice pulito sullo schiacciamento dei bachi E avrà difficoltà a implementare nuove funzionalità - debito tecnico puoi schiacciarti.

    
risposta data 20.04.2018 - 14:42
fonte
1

Va bene. Ho usato codice in FoxPro, dove ho avuto un blocco TRY..CATCH quasi in ogni grande funzione. Ora, codice in JavaScript / LiveScript e controllo raramente i parametri nelle funzioni "interne" o "private".

"Quanto controllare" dipende dal progetto / lingua scelti più di quanto non dipenda dalla tua competenza in codice.

    
risposta data 20.04.2018 - 10:44
fonte