Come posso definire e misurare la semplicità nel codice?


13

Ci sono molte risposte nella mia precedente domanda sulla semplicità relative alla leggibilità che mi hanno aiutato a vedere la mia definizione e comprensione della semplicità nel codice era, molto probabilmente, errata.

Come posso definire la semplicità nel codice? Quali misure e metriche software sono disponibili per misurare la semplicità del codice?


2
@MarkTrapp Esistono altri modi per discutere della semplicità del codice senza argomenti dell'ingegneria del software empirico, argomenti con cui ho molta meno familiarità. Ad esempio, discutere della semplicità in termini di capacità di scrivere test automatizzati. Le mie capacità e conoscenze mi consentono di rispondere a questa domanda dal punto di vista di un ingegnere informatico esperto, mentre altri possono rispondere da prospettive alternative. L'aggiunta di tale affermazione alla domanda limita significativamente il numero di risposte utili, rendendolo (IMO) troppo localizzato. Se vuoi aggiungerlo, puoi, ma questa è una buona domanda così com'è.
Thomas Owens

2
@ThomasOwens Le domande reali hanno risposte , non idee o opinioni. Restringere l'ambito in modo che tutti interpretino come rispondere alla domanda nello stesso modo è esattamente ciò che riguarda Stack Exchange. Potrebbe esserci più di un approccio per risolvere il problema, ma esiste un solo problema dichiarato in modo inequivocabile.

Allo stato attuale, ci sono pochissime risposte a questa domanda (la mia risposta affronta il punto di vista empirico dell'ingegneria del software, con le metriche comuni - probabilmente ce ne sono altre). Non ha senso escludere risposte che forniscono valide alternative da altre prospettive, ed è ciò che fa la formulazione di questa domanda. Non sono pienamente d'accordo con queste modifiche e la domanda dovrebbe essere ripristinata nella sua forma originale.
Thomas Owens

@MarkTrapp Il problema non è chiaro: come posso determinare la semplicità del codice? Ci sono molte buone risposte. Il mio è usare tecniche di ingegneria del software empiriche per misurare la complessità. Un altro potrebbe essere quello di scrivere test automatizzati e se è difficile scrivere buoni test, il codice è complesso - una risposta perfettamente valida. Potrebbero essercene altri di cui non sono a conoscenza. Se è necessario misurare la complessità / semplicità di una base di codice, la domanda deve essere formulata in modo tale da consentire la presentazione di tutte le alternative in modo che il richiedente possa scegliere la soluzione migliore per il suo caso specifico.
Thomas Owens

Risposte:


16

Le metriche più comuni per misurare la complessità (o semplicità, se si considera la semplicità l'opposto della complessità) sono la complessità ciclomatica di McCabe e le metriche della complessità di Halstead .

La complessità ciclomatica misura il numero di percorsi distinti attraverso una data unità, generalmente un metodo o una funzione, sebbene possa anche essere calcolata su una classe. Con l'aumentare del numero di percorsi, diventa più difficile ricordare il flusso di dati attraverso un determinato modulo, che è correlato al concetto di memoria di lavoro . Un'elevata complessità ciclomatica tende a indicare difficoltà nella capacità di testare un modulo: sono necessari più casi di test per coprire i vari percorsi attraverso il sistema. Ci sono stati anche studi che hanno collegato un'elevata complessità ciclomatica a tassi di difetti elevati. Tipicamente, una complessità ciclomatica di 10 indica che un'unità deve essere rivista e possibilmente refactored.

Le misure di complessità di Halstead utilizzano gli input di operatori e operandi totali e distinti per calcolare il volume, la difficoltà e lo sforzo di un pezzo di codice. La difficoltà, che è il (numero di operatori unici / 2) * (numero totale di operandi / numero di operandi unici), è legata alla capacità di leggere e comprendere il codice per attività come l'apprendimento del sistema o l'esecuzione di una revisione del codice. Ancora una volta, puoi contare questo a livello di sistema, a livello di classe o a livello di metodo / funzione. Ci sono alcuni post sul calcolo di queste misurazioni qui e qui .

Il semplice conteggio delle righe di codice può anche darti un'idea di complessità. Più righe di codice significano che c'è più da leggere e comprendere in un modulo. Sarei titubante nell'usarlo come misura autonoma. Invece, lo userei con altre misurazioni, come il numero di difetti in un dato modulo per ottenere la densità dei difetti. Un'alta densità di difetti potrebbe indicare problemi nella scrittura dei test e nell'esecuzione delle revisioni del codice, che possono essere o meno causati da codice complesso.

Fan-in e fan-out sono altre due metriche, correlate al flusso di dati. Come definito qui , fan in è la somma delle procedure chiamate, i parametri letti e le variabili globali lette e fan out è la somma delle procedure che chiamano una determinata procedura, parametri scritti in (esposti agli utenti esterni, passati per riferimento), e variabili globali scritte. Ancora una volta, un alto fan-in e fan-out potrebbero essere indicativi di un modulo che potrebbe essere difficile da capire.

In paradigmi specifici, potrebbero esserci anche altre misure o metriche utili. Ad esempio, nel mondo orientato agli oggetti, il monitoraggio dell'accoppiamento (desiderio basso), della coesione (desiderio alto) e della profondità dell'eredità (desiderio basso) può essere utilizzato per valutare quanto sia semplice o complicato un sistema.

Naturalmente, è importante rendersi conto che molte misure e metriche sono semplicemente indicatori. Devi usare il tuo giudizio per determinare se è necessario refactoring per aumentare la semplicità o se non vale la pena farlo. È possibile effettuare le misurazioni, calcolare le metriche e conoscere il codice, ma non si desidera progettare il sistema in base ai numeri. Alla fine, fai ciò che ha senso.


5
So che l'hai menzionato, ma è importante sottolineare che la complessità ciclomatica è davvero utile solo a livello di funzione / metodo e diventa significativamente più soggettiva / inutile a livelli più alti.
Ryathal,

Il problema è che mentre queste misure sono buone una guida generale. Esistono diversi casi in cui i programmi "cattivi" ottengono buoni risultati, ad esempio con una dozzina di funzioni, in cui una singola funzione con altri due parametri sarebbe sufficiente e, al contrario, molti programmi "buoni" che sono soluzioni ben scritte per un problema complesso possono segnare male.
James Anderson,

@James L'ho esplicitamente sottolineato. Ogni misurazione o metrica deve essere presa nel contesto come indicatore che qualcosa dovrebbe essere considerato. Ci vuole il giudizio di un ingegnere per determinare se è necessaria un'azione correttiva e quale sia l'azione. Tuttavia, a meno che non si raccolgano attivamente dati, non esiste un modo empirico di conoscere potenziali problemi.
Thomas Owens

7

Invece di guardare un modo formale per definire la semplicità, preferirei definire la semplicità come un attributo della qualità della scrittura del codice.

Non sto mettendo un po 'di semplicità ma quando chiami qualcosa di semplice o no.

1. Code Traversal:
quanto è facile navigare nel codice? È facile individuare dove sono scritte le funzioni API? È facile capire i flussi di chiamate, ad esempio quali metodi chiamano gli altri (e perché) - ci sono macchine a stato buono implementate o algoritmi ben identificati?

Quando l'attraversamento del codice è semplice, il codice è semplice da seguire.

2. Denominazione
Mentre altri standard di codifica aiutano a rendere il codice più pulito, la cosa più importante è la denominazione di classi / istanze di oggetto / variabili / metodi. L' uso di nomi chiari e inequivocabili ha chiaramente un grande impatto sulla semplicità del codice. Quando è difficile identificare un nome semplice, è un segno che potresti voler ripensare l'idea che sia quella variabile / metodo.

3. Interpretazione e riferimenti
Ognuno dei tuoi metodi ha un ruolo chiaro da svolgere. Ogni variabile / attributo è facile da determinare il ruolo che stanno giocando? Quando un pezzo di codice fa qualcosa che implica presupposti o influisce su un insieme di variabili non correlate, può diventare un incubo per la manutenzione.

4. Dipendenza o accoppiamento
Questo è difficile da giudicare solo guardando il codice, ma diventa molto evidente se qualcuno cerca di correggere i tuoi bug. Quando alcune altre cose cambiano in qualche altro oggetto, l'operazione qui cambia? Questi cambiamenti sono evidenti? Hai bisogno di cambiare l'API così spesso per ospitare cose. Questi suggeriscono che le relazioni intermodule non sono semplici

5. Input Utente o Applicazioni
Finalmente quanto sono semplici gli input o l'applicazione dell'utente accettati nell'API / UI? Quando più utenti / applicazioni possibili (per scopi diversi) devono darti: sono ovvi? Ci sono stati / dettagli che non sono correlati all'astrazione più alta ma continuano comunque a passare all'interfaccia?

Una semplice domanda che generalmente vorrei porre è la seguente: se invece di un programma, se avessi chiesto la stessa funzione che un essere umano avrebbe svolto, avrei riempito queste informazioni su un modulo cartaceo ? In caso contrario, non sono abbastanza semplice qui.

Non dirò che questo elenco è esaustivo, ma credo che i criteri siano quanto sia facile o difficile usare e modificare il software. È semplice.


1
Ha chiesto misure e metriche. Questi sono soggettivi, quindi non penso che siano molto importanti.
psr il

@psr sono d'accordo con te. Era anche molto evidente dalla risposta dalla risposta di Thomas. Tuttavia, ha menzionato la semplicità relativa alla leggibilità . La risposta di Thomas si occupa della complessità ciclomatica: questo ti dice quanto sia complesso testare il codice e non quanto sia complesso in termini di leggibilità e possa estenderne la manutenibilità . Questi sono due concetti molto diversi. Questo è esattamente il motivo per cui ho scritto questa risposta per mettere la netta contraddizione. Sfortunatamente per quanto ne so, non esistono metriche che si riferiscano alla semplicità del codice in termini di leggibilità.
Dipan Mehta,

"usa nomi che sono impossibili da capire" - IMHO punta troppo in alto, verso un obiettivo irrealistico e impossibile. Preferirei non provare ad essere così preciso e dire semplicemente "usa nomi chiari e non ambigui".
Péter Török,

@ PéterTörök Sono d'accordo. Credo che di solito, in molte organizzazioni, regole molto ben definite di convenzioni di denominazione e ancora una certa confusione circa l' intensione delle particolari persiste variabile. Quindi l'enfasi era di dire che la chiarezza dello scopo equivale alla semplicità anziché a una regola formale. Forse sono andato in mare per come ho descritto. Grazie.
Dipan Mehta,

@Dipan La complessità ciclomatica è legata alla leggibilità del codice, attraverso la memoria di lavoro. Il codice con elevata complessità ciclomatica (o anche solo un'elevata profondità del blocco) è difficile da tenere nella memoria di lavoro, quindi più difficile da leggere direttamente.
Thomas Owens

0

Non sono a conoscenza di buone metriche esistenti per la semplicità del codice (ciò non significa che non esistano - solo che non li conosco). Potrei proporne alcuni, forse alcuni aiuteranno:

  • Semplicità delle funzioni linguistiche utilizzate: se la lingua ha caratteristiche che potrebbero essere considerate "avanzate" e "semplici", è possibile contare il numero di occorrenze delle funzioni avanzate. Il modo in cui definisci "avanzato" potrebbe essere un po 'più soggettivo. Suppongo che qualcuno potrebbe dire che è anche come misurare la "intelligenza" di un programma. Un esempio comune: alcuni potrebbero dire che l' ?:operatore dovrebbe essere una funzionalità "avanzata", altri potrebbero non essere d'accordo. Non so quanto sia facile scrivere uno strumento in grado di verificare questo.

  • Semplicità dei costrutti all'interno del programma: è possibile misurare il numero di parametri accettati da una funzione. Se hai> n % di tutte le funzioni con> m parametri, puoi scegliere di contarlo come non semplice, a seconda di come definisci n e m (forse n = 3 e m = 6?). Penso che ci siano alcuni strumenti di analisi statica che possono misurare questo - penso che JTest abbia semplicemente misurato le funzioni con parametri > m .

  • Potresti provare a contare il numero di loop nidificati o strutture di controllo. Questo penso che in realtà non sia una cattiva metrica e penso che ci sia un nome per questo (non ricordo la parte superiore della mia testa). Ancora una volta, penso che ci siano strumenti (di nuovo, come JTest) che possono misurare questo, fino a un certo punto.

  • Potresti provare a misurare la "rifattabilità". Se il tuo codice contiene molti pezzi di codice che potrebbero essere refactored ma non lo sono , forse potrebbe non essere semplice. Ricordo anche da quando ho lavorato con JTest che ha provato a misurare anche questo, ma ricordo che in questo caso non ero spesso d'accordo, quindi YMMV.

  • Potresti provare a misurare il numero di strati tra diverse parti del tuo sistema. Ad esempio: quante diverse parti di codice toccheranno i dati che provengono da un modulo Web prima che vengano archiviati nel database? Questo potrebbe essere difficile da misurare correttamente ...


2
Credo che il numero 3 si chiami profondità del blocco. È anche correlato alla complessità ciclomatica se sono coinvolte strutture di controllo decisionale.
Thomas Owens

Nessuna spiegazione del downvote?
FrustratedWithFormsDesigner il

Non posso essere d'accordo con la "semplicità delle funzionalità linguistiche". Funzionalità avanzate sono lì per semplificare il codice. L'uso delle sole funzionalità di base oscurerà ciò che il codice sta effettivamente facendo, portando inevitabilmente a perdite di livelli di astrazione. Le funzionalità linguistiche avanzate consentono di esprimere livelli più elevati di astrazione, lasciando il codice molto più denso e leggibile. Più funzionalità avanzate stai usando (saggiamente, ovviamente), meglio è per semplicità. Basta confrontare un codice, ad esempio, in Matlab (che in effetti è "avanzato") con un codice Fortran simile fatto solo di funzionalità di base.
SK-logic

E non sarò d'accordo anche con un numero di livelli metrici. Se riesci a fare qualcosa in una dozzina di passaggi banali e puliti o in una trasformazione contorta, meglio farlo in un numero di passaggi. Molti strati semplici e chiaramente separati sono molto meglio (e più semplici) di un singolo strato ritorto.
SK-logic,

@ SK-logic: immagino che avrei dovuto chiamarla "intelligenza", più vicina a ciò che intendevo. Direi solo che cose come ?:un problema sono nidificate in profondità 5. Per quanto riguarda i livelli, i livelli ben separati sono meglio di uno strato contorto. Ma 7 livelli per lo più ridondanti, quando erano necessari solo 2 o 3 è una cosa negativa.
FrustratedWithFormsDesigner
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.