Scrierea unui cod elegant și ușor de citit

În acest tutorial, vă vom da nouă tehnici practice pentru scrierea unui cod elegant și lizibil. Nu vom vorbi despre arhitecturi specifice, limbi sau platforme. Accentul se pune pe scrierea unui cod mai bun. Să începem.

"Măsurarea progresului de programare de către liniile de cod este ca măsurarea progresului construirii aeronavelor în funcție de greutate." - Bill Gates

Introducere

Dacă sunteți dezvoltator, atunci probabil că ați fost momente când ați scris un cod și, după câteva zile, săptămâni sau luni, ați privit înapoi și v-ați spus "Ce face acest cod?" Răspunsul la această întrebare ar fi putut fi "Nu prea știu!" În acest caz, singurul lucru pe care îl puteți face este să treceți prin codul de la început până la sfârșit, încercând să înțelegeți ce gândiți când l-ați scris.

Acest lucru se întâmplă mai ales atunci când suntem leneși și dorim doar să implementăm acea nouă funcție pe care a solicitat-o ​​clientul. Vrem doar să facem treaba cu un efort cât mai mic posibil. Și când funcționează, nu ne pasă de codul însuși, deoarece clientul nu va vedea vreodată urât adevăr, să nu mai vorbim de ea. Dreapta? Gresit. Aceste zile, colaborarea cu software-ul a devenit implicită și oamenii vor vedea, citi și vor inspecta codul pe care îl scrieți. Chiar dacă codul dvs. nu este examinat de colegii dvs., ar trebui să faceți un obicei pentru a scrie cod clar și lizibil. Mereu.

De cele mai multe ori, nu lucrați singuri la un proiect. Vom vedea frecvent codul urât cu variabile care au nume precum eu, A, p, pro, și RQS. Și dacă se face într-adevăr rău, acest model este vizibil în întregul proiect. Dacă sună familiar, atunci sunt destul de sigur că ți-ai pus întrebarea: "Cum poate această persoană să scrie coduri ca asta?" Bineînțeles, acest lucru vă face cu atât mai mult recunoscător atunci când întâlniți un cod clar, lizibil și chiar frumos. Codul clar și curat poate fi citit în câteva secunde și vă poate economisi mult timp și pe colegii dumneavoastră. Aceasta ar trebui să fie motivația dvs. pentru scrierea codului de calitate.

1. Ușor de înțeles

Cu toții suntem de acord că acest cod ar trebui să fie ușor de înțeles. Dreapta? Primul exemplu se concentrează pe spațierea. Să aruncăm o privire la două exemple.

 returnați sexul == "1"? greutate * (înălțime / 10): greutate * (înălțime * 10);
 dacă (sex == "1") greutate retur * (înălțime / 10);  altceva retur greutate * (înălțime * 10); 

Chiar dacă rezultatul acestor exemple este identic, ele par diferite. De ce ar trebui să utilizați mai multe linii de cod dacă puteți scrie mai puțin? Să explorăm alte două exemple, pariez că vă vedeți frecvent.

 pentru (Node * node = list-> cap; nod! = NULL; node = nod -> următor) print (nod -> date);
 Node * node = list-> cap; dacă (node ​​== NULL) retur; în timp ce (node-> next! = NULL) Print (nod-> date); nod = nod -> următor;  dacă (nod! = NULL) Imprimați (nod -> date);

Din nou, rezultatul acestor exemple este identic. Care e mai bine? Și de ce? Mai puține linii de cod înseamnă un cod mai bun? Vom revizui această întrebare mai târziu în acest tutorial.

2. Este mai micã întotdeauna mai bunã?

În știința calculatoarelor, auziți adesea expresia "mai puțin este mai mult". În general, dacă puteți rezolva o problemă în mai puține linii de cod, cu atât mai bine. Probabil că vă va lua mai puțin timp pentru a înțelege o clasă de 200 de linii decât o clasă de 500 de linii. Cu toate acestea, este întotdeauna adevărat acest lucru? Consultați următoarele exemple.

 rezervare ((! room = FindRoom (room_id))) || ! Zi-> isOccupied ());
 cameră = FindRoom (room_id); dacă (cameră! = NULL) rezervare (! room-> isOccupied ());

Nu sunteți de acord că al doilea exemplu este mai ușor de citit și de înțeles? Trebuie să puteți optimiza lizibilitatea. Desigur, ați putea adăuga câteva comentarii la primul exemplu pentru a face mai ușor de înțeles, dar nu este mai bine să omiteți comentariile și să scrieți un cod mai ușor de citit și de înțeles?

 // Determinați unde să aruncați monstrul de-a lungul axei Y CGSize winSize = [CCDirector sharedDirector] .winSize; int minY = monster.contentSize.width / 2; int maxY = winSize.width - monster.contentSize.width / 2; int intervalY = maxY - minY; int actuală = (arc4random ()% intervalY) + minY;

3. Denumirea

Alegerea denumirilor descriptive pentru lucruri precum variabilele și funcțiile este un aspect cheie al scrierii codului lizibil. Vă ajută atât colegii, cât și pe voi înșivă să înțelegeți rapid codul. Denumirea unei variabile tmp nu vă spune altceva decât că variabila este temporară dintr-un anumit motiv, care nu este altceva decât o idee educată. Nu se spune dacă variabila stochează un nume, o dată, etc.

Un alt exemplu bun este numirea unei metode Stop. Nu este un nume rău în sine, dar asta depinde într-adevăr de implementarea metodei. Dacă efectuează o operație periculoasă care nu poate fi anulată, vă recomandăm să o redenumiți ucide sau pauză dacă operația poate fi reluată. Ai idee?

Dacă lucrați cu o variabilă pentru greutatea cartofilor, de ce ați spune-o tmp? Când revedeți acea bucată de cod câteva zile mai târziu, nu veți aminti ce tmp este folosit pentru.

Nu spunem asta tmp este un nume rău pentru o variabilă, deoarece există momente când tmp este perfect rezonabil ca nume de variabilă. Uitați-vă la următorul exemplu în care tmp nu este deloc o alegere rea.

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

În exemplul de mai sus, tmp descrie ceea ce face, stochează temporar o valoare. Nu este transferată la o funcție sau la o metodă și nu este incrementată sau modificată. Are o durată de viață bine definită și niciun dezvoltator experimentat nu va fi aruncat de numele variabilei. Uneori, totuși, este vorba doar de lenevie. Uitați-vă la următorul exemplu.

 NSString * tmp = nume de utilizator; tmp + = "" + user.phone_number; tmp + = "" + user.email; ... [șablon setObject: tmp pentruKey: @ "user_info"];

Dacă tmp stochează informațiile utilizatorului, de ce nu este denumit userinfo? Denumirea corectă a variabilelor, funcțiilor, metodelor, clasei etc. este importantă atunci când scrieți un cod lizibil. Nu numai că vă face codul mai ușor de citit, ci vă va economisi timp în viitor.

Obiectivul C este destul de verbos, dar este foarte ușor de citit. Apple utilizează o convenție bine definită de numire pe care o puteți adopta în majoritatea limbajelor de programare. Puteți citi mai multe despre această convenție de numire în Programarea cu Obiectiv-C.

4. Adăugați semnificație în nume

După cum am văzut în sfatul anterior, este important să alegeți nume cu înțelepciune. Cu toate acestea, este la fel de important să adăugați înțeles numele pe care îl utilizați pentru variabile, funcții, metode etc. Nu numai că acest lucru evită confuzia, ci face mai ușor să înțelegeți codul pe care îl scrieți. Alegerea unui nume care are sens are loc aproape ca adăugând metadate la o variabilă sau la o metodă. Alegeți nume descriptive și evitați cele generice. Cuvantul adăuga, de exemplu, nu este întotdeauna ideal, după cum puteți vedea în exemplul următor.

 bool addUser (Utilizator u) ...

Nu este clar ce Adăugați utilizator ar trebui să facă. Adaugă un utilizator la o listă de utilizatori, la o bază de date sau la o listă de persoane invitate la o petrecere? Comparați asta cu registerUser sau signupUser. Acest lucru are mai mult sens. Dreapta? Uitați-vă la următoarea listă pentru a obține o idee mai bună despre ceea ce conducem.

Cuvânt Sinonime
do face, executa, executa, compune, adauga start lansați, creați, începeți, deschideți exploda detona, arunca în aer, porniți, izbucni

5. Dimensiune nume

O mulțime de programatori nu le plac nume lungi, pentru că sunt greu de reținut și dificil de scris. Bineînțeles, un nume nu ar trebui să fie de lungă durată newClassForNavigationControllerNamedFirstViewController. Acest este greu de reținut și face pur și simplu codul tău urât și de nerepătat.

După cum am văzut mai devreme, denumirile opuse, scurte, nici nu sunt bune. Care este dimensiunea potrivită pentru un nume de variabilă sau de metodă? Cum decideți între denumirea unei variabile Len, lungime, sau user_name_length? Răspunsul depinde de context și de entitatea la care este legat numele.

Numele lungi nu mai sunt o problemă atunci când folosiți un IDE modern (Mediu de dezvoltare integrată). Finalizarea codului vă ajută să evitați greșelile și, de asemenea, face sugestii pentru a vă aminti numele mai puțin dureros.

Puteți utiliza nume scurte (er) dacă variabila este locală. În plus, este recomandat să utilizați nume mai scurte pentru variabilele locale pentru a vă menține codul lizibil. Uitați-vă la următorul exemplu.

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

6. Denumirea booleanelor

Booleanii pot fi dificil de numit, deoarece pot avea un înțeles diferit în funcție de modul în care citiți sau interpretați numele. În următorul fragment de cod, read_password poate însemna că parola a fost citită de către program, dar poate însemna și că programul ar trebui să citească parola.

 BOOL readPassword = DA;

Pentru a evita această problemă, puteți redenumi booleanul de mai sus didReadPassword pentru a indica faptul că parola a fost citită sau shouldReadPassword pentru a arăta că programul trebuie să citească parola. Acesta este ceva ce vedeți foarte mult în obiectivul C, de exemplu.

7. Pentru a comenta sau a nu comenta

Adăugarea de comentarii la cod este importantă, dar este la fel de important să le folosim cu ușurință. Acestea ar trebui folosite pentru a vă ajuta pe cineva să vă înțeleagă codul. Cu toate acestea, citirea comentariilor necesită timp și dacă un comentariu nu adaugă multă valoare, atunci timpul este pierdut. Următorul fragment de cod arată cum nu să utilizeze comentarii.

 // Aceasta se întâmplă atunci când se primește avertizarea de memorie - (void) didReceiveMemoryWarning [super didReceiveMemoryWarning]; // Aruncați orice resurse care pot fi recreate.  // Validarea câmpurilor - (BOOL) validateFields 

Sunt aceste fragmente de cod utile pentru dvs.? Răspunsul este probabil "Nu". Comentariile din exemplele de mai sus nu adaugă informații suplimentare, mai ales că numele metodelor sunt deja foarte descriptive, ceea ce este obișnuit în Obiectiv-C. Nu adăugați comentarii care explică evident. Uitați-vă la următorul exemplu. Nu este o utilizare mai bună a comentariilor?

 // Determinați viteza monștrii int minDuration = 2.0; int maxDuration = 8,0; int rangeDurație = maxDurație - minDurație; int actualDuration = (arc4random ()% intervalDurație) + minDurație;

Comentariile de acest fel fac foarte ușor navigarea rapidă și eficientă a unei baze de coduri. Aceasta vă scutește de a fi nevoită să citiți codul și vă ajută să înțelegeți logica sau algoritmul.

8. Stil și coerență

Fiecare limbă sau platformă are un ghid de stil (sau mai multe) și chiar majoritatea companiilor au unul. Puneți acoladele curlate ale unei metode Obiectiv-C pe o linie separată sau nu?

 - (void) calculateOffset 
 - (void) calculateOffset 

Răspunsul este că nu contează. Nu există un răspuns corect. Desigur, există ghiduri de stil pe care le puteți adopta. Ceea ce este important este că codul dvs. este consistent în ceea ce privește stilul. Chiar dacă acest lucru nu poate afecta calitatea codului dvs., cu siguranță afectează lizibilitatea și, cel mai probabil, va deranja dracu de colegii dvs. sau de oricine vă va citi codul. Pentru majoritatea dezvoltatorilor, codul urât este cel mai rău tip de cod.

9. Metode și funcții focalizate

O greșeală obișnuită în rândul dezvoltatorilor încearcă să strângă cât mai multă funcționalitate în funcții și metode. Acest lucru funcționează, dar este inelegant și face depanare o durere în gât. Viața dvs. - și cea a colegilor dvs. - va deveni mult mai ușoară dacă veți rupe probleme mai mari în biți mici și veți aborda acele biți în funcții sau metode separate. Uitați-vă la următorul exemplu în care scriem o imagine pe disc. Acest lucru pare a fi o sarcină trivială, dar este mult mai mult pentru asta dacă vrei să o faci bine.

 - (BOOL) saveToImage: (UIImage *) imagine cuFileName: (NSString *) nume_fișier BOOL rezultat = NU; NSString * documents = nil; Căile NSArray * = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); dacă (paths.count) documents = [paths objectItIndex: 0]; NSString * basePath = [documente stringByAppendingPathComponent: @ "Arhiva"]; dacă [! [[NSFileManager defaultManager] fileExistsAtPath: basePath]) NSError * error = nil; [[NSFileManager defaultManager] createDirectoryAtPath: basePath cuIntermediateDirectories: YES atribute: eroare zero: & eroare]; dacă (! eroare) NSString * filePath = [basePath stringByAppendingPathComponent: fileName]; rezultat = [UIImageJPEGRpresentație (imagine, 8.0) writeToFile: filePath atomic: YES];  altceva NSLog (@ "Imposibil de creat director din cauza erorii% @ cu informația utilizatorului% @", eroare, error.userInfo);  retur; 

Dacă o unitate de cod încearcă să facă prea mult, deseori ajungeți la declarații condiționate profund imbricate, la o mulțime de verificări de eroare și la declarații condiționale prea complexe. Această metodă face trei lucruri, preluă calea directorului de documente al aplicației, preluă și creează calea pentru directorul de arhive și scrie imaginea pe disc. Fiecare sarcină poate fi pusă în propria sa metodă după cum se arată mai jos.

 - (BOOL) saveToImage: (UIImage *) imagine cuFileName: (NSString *) nume_fișier NSString * archivesDirectory = [self applicationArchivesDirectory]; dacă (! archivesDirectory) returnați NO; // Crearea căii NSString * filePath = [arrayDirectory stringByAppendingPathComponent: fileName]; // Scrierea imaginii înapoi la disc [UIImageJPEGRpresentation (image, 8.0) writeToFile: filePath atomic: YES]; 
 - (NSString *) cerereDocumentsDirectory NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); retur paths.count? [căi obiectAtIndex: 0]: nul; 
 - (NSString *) cerereaArchivesDirectory NSString * documentsDirectory = [self applicationDocumentsDirectory]; NSString * archivesDirectory = [documenteDirectory stringByAppendingPathComponent: @ "Arhive"]; NSFileManager * fm = [NSFileManager implicitManager]; dacă ! [fm fileExistsAtPath: archivesDirectory]) NSError * error = zero; [fm createDirectoryAtPath: arhiveDirector cuIntermediateDirectories: YES atribute: eroare zero: & eroare]; dacă (eroare) NSLog (@ "Imposibil de creat director din cauza erorii% @ cu informația utilizatorului% @", eroare, error.userInfo); retur nul;  retur arhiveDirector; 

Acest lucru este mult mai ușor de depanat și de întreținut. Puteți chiar reutiliza applicationDocumentsDirectory în alte locuri ale proiectului, ceea ce reprezintă un alt beneficiu al depășirii problemelor mai mari în piese ușor de gestionat. Codul de testare devine mult mai ușor.

Concluzie

În acest articol, am analizat mai îndeaproape codul care poate fi citit, selectând cu înțelepciune nume pentru variabile, funcții și metode, fiind coerent în scrierea codului și rupând problemele complexe în bucăți ușor de gestionat. Dacă aveți întrebări sau feedback, vă rugăm să lăsați un comentariu mai jos.

Cod