Scrivere codice elegante e leggibile

In questo tutorial, ti forniremo nove tecniche pratiche per scrivere codice elegante e leggibile. Non parleremo di architetture, linguaggi o piattaforme specifici. L'attenzione si concentra sulla scrittura di codice migliore. Iniziamo.

"Misurare l'avanzamento della programmazione per linee di codice è come misurare il progresso della costruzione dell'aeromobile in base al peso." - Bill Gates

introduzione

Se sei uno sviluppatore, probabilmente ci sono state delle volte in cui hai scritto codice e, dopo alcuni giorni, settimane o mesi, hai guardato indietro e hai detto a te stesso "Cosa fa questo codice?" La risposta a quella domanda potrebbe essere stata "Io davvero non lo so!" In quel caso, l'unica cosa che puoi fare è passare il codice dall'inizio alla fine, cercando di capire cosa stavi pensando quando lo hai scritto.

Questo accade soprattutto quando siamo pigri e vogliamo solo implementare quella nuova funzione richiesta dal cliente. Vogliamo solo portare a termine il lavoro con il minor sforzo possibile. E quando funziona, non ci interessa il codice stesso, perché il client non vedrà mai il brutto verità, figuriamoci capirlo. Destra? Sbagliato. In questi giorni, la collaborazione con il software è diventata l'impostazione predefinita e la gente vedrà, leggerà e ispezionerà il codice che scrivi. Anche se il tuo codice non è esaminato dai tuoi colleghi, dovresti prendere l'abitudine di scrivere codice chiaro e leggibile. Sempre.

Il più delle volte, non lavori da solo su un progetto. Vediamo spesso codice brutto con variabili con nomi simili io, un, p, professionista, e rqs. E se davvero va male, questo schema è visibile in tutto il progetto. Se questo suona familiare, allora sono abbastanza sicuro che ti sei posto la domanda: "Come può questa persona scrivere un codice come questo?" Naturalmente, questo ti rende ancora più grato quando ti imbatti in codice chiaro, leggibile e persino bello. Il codice chiaro e pulito può essere letto in pochi secondi e può far risparmiare molto tempo a te e ai tuoi colleghi. Questa dovrebbe essere la tua motivazione per scrivere un codice di qualità.

1. Facile da capire

Siamo tutti d'accordo sul fatto che il codice dovrebbe essere facile da capire. Destra? Il primo esempio si concentra sulla spaziatura. Diamo un'occhiata a due esempi.

 return gender == "1"? peso * (altezza / 10): peso * (altezza * 10);
 if (gender == "1") return weight * (height / 10);  else return weight * (height * 10); 

Anche se il risultato di questi esempi è identico, sembrano molto diversi. Perché dovresti usare più righe di codice se riesci a scrivere di meno? Esploriamo altri due esempi, qualcosa che scommetto che vedi spesso.

 per (Nodo * nodo = elenco-> testa; nodo! = NULL; nodo = nodo-> successivo) stampa (nodo-> dati);
 Nodo * nodo = elenco-> testa; se (node ​​== NULL) restituisce; while (node-> next! = NULL) Print (node-> data); nodo = nodo-> successivo;  if (node! = NULL) Stampa (nodo-> dati);

Ancora una volta, il risultato di questi esempi è identico. Quale è meglio? E perché? Un minor numero di righe di codice significa un codice migliore? Torneremo su questa domanda più avanti in questo tutorial.

2. È più piccolo sempre meglio?

Nell'informatica, spesso si sente la frase "less is more". In generale, se riesci a risolvere un problema con meno righe di codice, meglio è. Probabilmente ci vorrà meno tempo per capire una classe a 200 linee rispetto a una classe a 500 righe. Tuttavia, è sempre vero? Dai uno sguardo ai seguenti esempi.

 prenotazione ((! room = FindRoom (room_id))) || ! Camera-> isOccupied ());
 room = FindRoom (room_id); se (room! = NULL) prenotazione (! room-> isOccupied ());

Non sei d'accordo sul fatto che il secondo esempio sia più facile da leggere e capire? Devi essere in grado di ottimizzare la leggibilità. Naturalmente, puoi aggiungere alcuni commenti al primo esempio per renderlo più facile da capire, ma non è meglio omettere i commenti e scrivere codice che è più facile da leggere e capire?

 // Determina dove generare il mostro lungo l'asse Y CGSize winSize = [CCDirector sharedDirector] .winSize; int minY = monster.contentSize.width / 2; int maxY = winSize.width - monster.contentSize.width / 2; int rangeY = maxY - minY; int actualY = (arc4random ()% rangeY) + minY;

3. Denominazione

La scelta di nomi descrittivi per cose come variabili e funzioni è un aspetto chiave della scrittura di codice leggibile. Aiuta sia i tuoi colleghi che te stesso a capire rapidamente il codice. Denominazione di una variabile tmp non ti dice altro se non che la variabile è temporanea per qualche ragione, che non è altro che un'ipotesi plausibile. Non dice se la variabile memorizza un nome, una data, ecc.

Un altro buon esempio è nominare un metodo Stop. Non è un brutto nome di per sé, ma dipende molto dall'implementazione del metodo. Se esegue un'operazione pericolosa che non può essere annullata, si consiglia di rinominarla uccidere o pausa se l'operazione può essere ripresa. Hai un'idea?

Se stai lavorando con una variabile per il peso delle patate, perché dovresti nominarla tmp? Quando rivedi quel pezzo di codice qualche giorno dopo, non ricorderai cosa tmp è usato per.

Non lo stiamo dicendo tmp è un brutto nome per una variabile, perché ci sono momenti in cui tmp è perfettamente ragionevole come nome di una variabile. Dai un'occhiata al seguente esempio in cui tmp non è affatto una cattiva scelta.

 tmp = first_potato; first_potato = second_potato; second_potato = tmp;

Nell'esempio sopra, tmp descrive cosa fa, memorizza temporaneamente un valore. Non viene passato a una funzione o metodo e non viene incrementato o modificato. Ha una durata ben definita e nessuno sviluppatore esperto verrà escluso dal nome della variabile. A volte, tuttavia, è semplicemente pigrizia. Dai un'occhiata al prossimo esempio.

 NSString * tmp = user.name; tmp + = "" + user.phone_number; tmp + = "" + user.email; ... [template setObject: tmp forKey: @ "user_info"];

Se tmp memorizza le informazioni dell'utente, perché non è chiamato userInfo? La corretta denominazione di variabili, funzioni, metodi, classi, ecc. È importante quando si scrive codice leggibile. Non solo rende più leggibile il tuo codice, ma ti farà risparmiare tempo in futuro.

Objective-C è abbastanza dettagliato, ma è molto facile da leggere. Apple utilizza una convenzione di denominazione ben definita che è possibile adottare nella maggior parte dei linguaggi di programmazione. Puoi leggere ulteriori informazioni su questa convenzione di denominazione in Programmazione con Objective-C.

4. Aggiungi significato ai nomi

Come abbiamo visto nel suggerimento precedente, è importante scegliere i nomi con saggezza. Tuttavia, è ugualmente importante aggiungere un significato ai nomi che usi per variabili, funzioni, metodi, ecc. Questo non solo aiuta a evitare confusione, rende più comprensibile il codice che scrivi. Scegliere un nome che abbia senso è quasi come aggiungere metadati a una variabile o un metodo. Scegli nomi descrittivi ed evita quelli generici. La parola Inserisci, per esempio, non è sempre l'ideale, come puoi vedere nel prossimo esempio.

 bool addUser (Utente u) ...

Non è chiaro cosa Aggiungi utente dovrebbe fare. Aggiunge un utente a un elenco di utenti, a un database o a un elenco di persone invitate a una festa? Confronta questo a registerUser o signupUser. Questo ha più senso. Destra? Dai un'occhiata al seguente elenco per avere un'idea migliore di cosa stiamo guidando.

parola Sinonimi
fare creare, eseguire, eseguire, comporre, aggiungere inizio lanciare, creare, iniziare, aprire esplodere detona, esplode, esplode, esplode

5. Dimensione del nome

A molti programmatori non piacciono i nomi lunghi, perché sono difficili da ricordare e scomodi da digitare. Naturalmente, un nome non dovrebbe essere ridicolmente lungo come newClassForNavigationControllerNamedFirstViewController. Questo è difficile da ricordare e rende semplicemente il tuo codice brutto e illeggibile.

Come abbiamo visto in precedenza, anche i nomi brevi e contrari non vanno bene. Qual è la dimensione giusta per un nome di variabile o metodo? Come decidi tra nominare una variabile len, lunghezza, o user_name_length? La risposta dipende dal contesto e dall'entità a cui è legato il nome.

I nomi lunghi non sono più un problema quando si utilizza un IDE moderno (Integrated Development Environment). Il completamento del codice ti aiuta a evitare errori di battitura e inoltre suggerisce di rendere meno fastidiosi i nomi dei ricordi.

È possibile utilizzare nomi brevi (er) se la variabile è locale. Inoltre, si consiglia di utilizzare nomi più brevi per le variabili locali per mantenere leggibile il codice. Dai un'occhiata al seguente esempio.

 NSString * link = [[NSString alloc] initWithFormat: @ "http: // localhost: 8080 / WrittingGoodCode / resources / GoodCode / getGoodCode /% @", idCode]; NSURL * infoCode = [NSURL URLWithString: link];

6. Denominazione di booleani

I booleani possono essere difficili da nominare, perché possono avere un significato diverso a seconda del modo in cui leggi o interpreti il ​​nome. Nel prossimo snippet di codice, read_password può significare che la password è stata letta dal programma, ma può anche significare che il programma dovrebbe leggere la password.

 BOOL readPassword = YES;

Per evitare questo problema, puoi rinominare il precedente booleano in didReadPassword per indicare che la password è stata letta o shouldReadPassword per mostrare che il programma ha bisogno di leggere la password. Questo è qualcosa che vedi molto in Objective-C, per esempio.

7. Per commentare o non commentare

Aggiungere commenti al codice è importante, ma è ugualmente importante usarli con parsimonia. Dovrebbero essere usati per aiutare qualcuno a capire il tuo codice. Leggere i commenti, tuttavia, richiede tempo e se un commento non aggiunge molto valore, allora quel tempo è sprecato. Il prossimo snippet di codice mostra come non usare i commenti.

 // Questo accade quando viene ricevuto un avviso di memoria - (void) didReceiveMemoryWarning [super didReceiveMemoryWarning]; // Elimina le risorse che possono essere ricreate.  // Convalida i campi - (BOOL) validateFields 

Ti sono utili questi frammenti di codice? La risposta è probabilmente "No" I commenti negli esempi di cui sopra non aggiungono ulteriori informazioni, soprattutto perché i nomi dei metodi sono già molto descrittivi, che è comune in Objective-C. Non aggiungere commenti che spieghino l'ovvio. Dai un'occhiata al prossimo esempio. Non è un uso molto migliore dei commenti?

 // Determina la velocità del mostro int minDuration = 2.0; int maxDuration = 8.0; int rangeDuration = maxDuration - minDuration; int actualDuration = (arc4random ()% rangeDuration) + minDuration;

Commenti come questo rendono molto facile navigare in un codice base in modo rapido ed efficiente. Ti evita di dover leggere il codice e ti aiuta a capire la logica o l'algoritmo.

8. Stile e coerenza

Ogni lingua o piattaforma ha una (o più) guida di stile e anche molte aziende ne hanno una. Metti le parentesi graffe di un metodo Objective-C su una linea separata o meno?

 - (void) calculateOffset 
 - (void) calculateOffset 

La risposta è che non importa. Non c'è una risposta giusta. Naturalmente, ci sono delle guide di stile che puoi adottare. Ciò che è importante è che il tuo codice sia coerente in termini di stile. Anche se ciò potrebbe non influire sulla qualità del tuo codice, sicuramente influisce sulla leggibilità e molto probabilmente infastidirà i tuoi colleghi o chiunque legga il tuo codice. Per la maggior parte degli sviluppatori, il brutto codice è il peggior tipo di codice.

9. Metodi e funzioni focalizzati

Un errore comune tra gli sviluppatori è quello di tentare di integrare tutte le funzionalità in funzioni e metodi. Funziona, ma è inelegante e rende il debug un dolore al collo. La tua vita - e quella dei tuoi colleghi - diventerà molto più facile se rompi i problemi più grandi in piccoli bit e affronta questi bit in funzioni o metodi separati. Dai un'occhiata al prossimo esempio in cui scriviamo un'immagine su disco. Questo sembra un compito banale, ma c'è molto di più se vuoi farlo bene.

 - (BOOL) saveToImage: (UIImage *) image withFileName: (NSString *) fileName BOOL result = NO; NSString * documents = nil; NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); if (paths.count) documents = [paths objectAtIndex: 0]; NSString * basePath = [documenti stringByAppendingPathComponent: @ "Archivio"]; if (! [[NSFileManager defaultManager] fileExistsAtPath: basePath]) NSError * error = nil; [[NSFileManager defaultManager] createDirectoryAtPath: basePath conIntermediateDirectories: YES attributi: nil error: & error]; if (! error) NSString * filePath = [basePath stringByAppendingPathComponent: fileName]; result = [UIImageJPEGRepresentation (image, 8.0) writeToFile: filePath atomically: YES];  else NSLog (@ "Impossibile creare la directory a causa dell'errore% @ con le informazioni utente% @.", errore, error.userInfo);  restituisce risultato; 

Se un'unità di codice tenta di fare troppo, spesso si finisce con affermazioni condizionali profondamente annidate, un sacco di verifiche degli errori e istruzioni condizionali eccessivamente complesse. Questo metodo fa tre cose, recupera il percorso della directory dei documenti dell'applicazione, recupera e crea il percorso per la directory degli archivi e scrive l'immagine sul disco. Ogni attività può essere inserita nel proprio metodo come mostrato di seguito.

 - (BOOL) saveToImage: (UIImage *) image withFileName: (NSString *) fileName NSString * archivesDirectory = [self applicationArchivesDirectory]; se (! archivesDirectory) restituisce NO; // Crea percorso NSString * filePath = [archivesDirectory stringByAppendingPathComponent: fileName]; // Scrivi Image to Disk return [UIImageJPEGRepresentation (image, 8.0) writeToFile: filePath atomically: YES]; 
 - (NSString *) applicationDocumentsDirectory NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); return paths.count? [percorsi objectAtIndex: 0]: nil; 
 - (NSString *) applicationArchivesDirectory NSString * documentsDirectory = [self applicationDocumentsDirectory]; NSString * archivesDirectory = [documentsDirectory stringByAppendingPathComponent: @ "Archives"]; NSFileManager * fm = [NSFileManager defaultManager]; if (! [fm fileExistsAtPath: archivesDirectory]) NSError * error = nil; [fm createDirectoryAtPath: archivesDirectory withIntermediateDirectories: YES attributes: nil error: & error]; if (errore) NSLog (@ "Impossibile creare la directory a causa dell'errore% @ con informazioni utente% @.", errore, error.userInfo); return nil;  return archivesDirectory; 

Questo è molto più facile da eseguire il debug e la manutenzione. Puoi persino riutilizzare il applicationDocumentsDirectory metodo in altri luoghi del progetto, che è un altro vantaggio di rompere i problemi più grandi in pezzi gestibili. Anche il codice di verifica diventa molto più semplice.

Conclusione

In questo articolo, abbiamo esaminato più da vicino la scrittura di codice leggibile scegliendo saggiamente nomi per variabili, funzioni e metodi, essendo coerenti nella scrittura del codice e rompendo problemi complessi in blocchi gestibili. Se avete domande o commenti, sentitevi liberi di lasciare un commento qui sotto.