ABONAMENTE VIDEO REDACȚIA
RO
EN
×
▼ LISTĂ EDIȚII ▼
Numărul 27
Abonament PDF

Clean code – Funcții

Radu Vunvulea
Solution Architect
@iQuest
PROGRAMARE

În ultimul articol din TSM, am descoperit împreună universul codului curat, prin "Clean Code" scrisă de Robert C. Martin. Am avut ocazia să aprofundăm subiectul denumirilor și să vedem cât de ușor pot lucrurile mici precum numele funcțiilor sau al variabilelor să îmbunătățească calitatea și lizibilitatea codului însuși.

Astăzi vom plonja mai adânc în Clean Code și vom discuta despre funcții ("Functions"). Acest mecanism simplu și de bază folosit pentru a scrie programe poate avea un impact nu numai asupra ușurinței cu care poate fi întreținut și extins un program, ci și asupra sănătății mintale a dezvoltatorilor. Nu uitați că metodele lungi vă vor face ochii să lăcrimeze.

Imaginați-vă o carte în care toate paragrafele sunt amestecate, mărimea caracterelor este diferită pentru fiecare dintre ele și o parte din ele au 20 de pagini. Cât de ușor ați putea citi această carte? Codul ar trebui să fie scris într-un fel care să ofere oamenilor șansa de a-l citi ca pe o carte, de la început până la sfârșit, în care fiecare logică diferită este grupată separat.

O poveste veselă

Îmi amintesc o dată când a trebuit să extind un cod existent scris de altcineva. Când am deschis soluția am găsit o singură clasă, cu 2 sau 3 metode, care avea în total în jur de 4.000 linii de cod. Estimările mele pentru sarcina aceasta au fost:

Managerul meu de proiect de la acea vreme a acceptat această estimare și mi-a dat undă verde pentru a lucra la el. Dar în final, am avut nevoie de 4x mai mult timp pentru a îndeplini sarcina, deoarece metodele în sine erau prea lungi și nu am putut face nimic fără a avea coșmaruri noaptea.

Așadar ce putem face pentru a îmbunătăți calitatea dezvoltatorilor și a software-ului nostru din perspectiva funcțiilor/metodelor?

Să fie scurte

Aceasta este prima și cea mai importantă regulă legată de funcții. Ar trebui să le păstrați cât mai scurte posibil. Explicația este destul de simplă: o funcție scurtă va face mai puțin (numai un singur lucru simplu). În plus de asta, va fi mai ușor de înțeles și de lucrat cu ea.

Întrebarea normală care ne vine în minte este "Cât de scurte?"

Din păcate, nu putem avea un număr magic cum ar fi 5 sau 20, pentru că este destul de greu să generalizezi. Lungimea unei metode depinde de factori multipli, cum ar fi convențiile codului. De exemplu, cât de des apeși enter pentru a adăuga o nouă linie (pentru fiecare { sau pentru fiecare afirmație logică și așa mai departe).

În general, dacă sfârșești prin a avea o metodă mai lungă de (???) linii de cod, atunci ar trebui să arunci o privire peste ea ca să vezi de ce este așa de lungă. Este din cauza convențiilor codului sau din cauză că există prea multă logică acolo?

Blocuri și indentare

În legătură cu IF (dacă), ELSE (alt), WHERE (unde), REPEAT (repetare) și alte astfel de funcționalități, nu ai vrea să te trezești cu un IF de 10 linii. Ar fi destul de greu de citit și de înțeles. Pentru astfel de cazuri, ar trebui să extragi controlul (check) într-o funcție diferită și să îl apelezi din comanda IF. Aplicând această regulă, veți avea comenzi ca IF sau WHERE care necesită numai o singură linie de cod.

Mai mult, vă veți îmbunătăți lizibilitatea și documentația codului. Pentru dezvoltatori va fi foarte ușor să înțeleagă ce face codul și ce ar trebui să facă controlul din spatele acelui IF sau WHERE.

Un singur lucru de făcut

Dacă citești o funcție lungă, îți dai seama că face mai mult de un lucru acolo. De exemplu, în aceeași funcție deschizi conexiunea DB, execuți o cerere, transformi rezultatul într-un alt tip și te ocupi de cazurile speciale. Fiecare dintre lucrurile acestea ar trebui făcute separat.

De aceea o funcție ar trebui să facă numai un singur lucru. Dacă ai nevoie să faci mai mult de un lucru, atunci ar trebui să le împarți în funcții separate.

Chiar dacă afirmația este atât de simplă, este destul de dificil să faci asta. Dacă descoperiți într-o funcție părți diferite ale codului care sunt grupate sau dacă puteți extrage o parte din aceasta într-o funcție separată cu un nume care are sens, atunci funcția face mai mult decât un singur lucru.

Un nivel de abstracție per Funcție

Acesta poate fi un mecanism care ne poate spune că funcția face prea multe lucruri. De exemplu, o funcție care procesează o entitate și de asemenea începe să se dividă în interior și să transfere unul dintre câmpurile entității, are mai mult de un nivel de abstracție.

Este destul de clar că ar trebui să extragi procesarea în lanț într-o altă funcție. În acest fel, fiecare funcție va avea un singur nivel de abstracție.

Comanda Switch

Povestea legată de comenzile switch este destul de lungă și vom vorbi despre ea cu altă ocazie. În cazul nostru, ar trebui să extragem logica din fiecare CASE pentru a separa funcțiile. Chiar dacă facem acest lucru, nu va fi ok, deoarece încălcăm principiul unicei responsabilități (Single Responsibility Principle).

Noi ar trebui să înlocuim comanda switch cu polimorfismul. Aceasta este soluția perfectă, dar există cazuri în care o astfel de soluție ar aduce în plus prea multă complexitate. Când este nevoie să folosești o comandă switch, ar trebui să o ascunzi cât de adânc posibil; în Clean Code, recomandarea este în spatele unui factor abstract.

Utilizați denumiri descriptive

Numele unei funcții ar trebui să descrie exact ceea ce face. Nici mai mult, nici mai puțin. De exemplu, o funcție cu nume precum DO, ACTION nu ne ajută prea mult, pentru că nu știm care este scopul lor.

Un nume ca, TriggerDoorLock (Declanșează blocare ușă) ne oferă toate informațiile de care avem nevoie pentru a știi ce face aceea funcție.

Găsirea unei denumiri bune este destul de dificilă și poate însemna un consum mare de energie. În plus, trebuie să fiți constanți și să încercați să utilizați același model de denumire atunci când există similarități.

Argumentele funcției

Câte argumente ar trebui să aibă o funcție? Cea mai bună valoare este 0, dar acest lucru nu este posibil întotdeauna. De fiecare dată când adăugați un argument nou, gândiți-vă la rolul său.

Când ajungeți să aveți mai mult de 3-4 argumente, poate că ceva e greșit. Ar trebui să le revizuiți și să vedeți dacă nu puteți muta funcția într-o altă locație sau să adăugați un alt nivel de abstracție.

Opțiunea OUT pentru argumente nu este recomandată întotdeauna și poate semnala că ceva este greșit acolo. De exemplu, metodele TryXXX verifică de obicei dacă poate fi făcută o conversie. Dacă aceasta poate fi efectuată cu succes, răspunsul este TRUE iar parametrul out va conține rezultatul conversiei. Aceasta ar putea semnala că metoda face prea multe lucruri - transformări și verificări.

Fără efecte secundare

Aceasta este situația în care funcția ta face mai mult decât un singur lucru, dar fără a spune clientului. De exemplu, o metodă READ care citește conținutul unui fișier și apoi îl șterge fără a notifica utilizatorul. În acest caz, utilizatorul ar trebui să fie înștiințat despre această acțiune sau, cel puțin, ar trebui să știe de ea din momentul în care face apelarea - ReadAndDelete (Citește și șterge).

Din cauza acestor efecte secundare, putem avea cuplări temporare. De exemplu, când funcția GoLeft poate fi apelată numai dacă a fost apelată StartEngine. Ar trebui să vă gândiți la o modalitate de a expune numai metodele care sunt disponibile la un anumit moment, fără a crea cuplări temporare. De exemplu, StartEngine poate returna un obiect care are numai comenzi ca GoLeft, GoRight, etc.

Comandați separarea cererii

O funcție ar trebui să facă numai un singur lucru. Nu ar trebui să aveți niciodată metode care să execute o cerere și în același timp o comandă. Aceste două acțiuni trebuie să fie separate întotdeauna, fără excepție.

Preferați excepțiile și nu erorile de cod

Producerea unui cod eronat generează două lucruri în plus de care trebuie să se ocupe dezvoltatorul/ clientul. El trebuie să cunoască harta fiecărui cod greșit și în același timp trebuie să verifice codul eronat produs.

Pentru aceste cazuri, proiectarea unei excepții este mai bună și va simplifica munca clienților. În plus, tratarea erorilor va fi 100% separată de logica ta.

Extrageți blocurile try/catch

Un cod care conține astfel de blocuri sunt destul de urâte și lungi. Din această cauză, toate aceste blocuri de cod ar trebui să fie extrase în funcții separate. Blocul TRY poate fi pus într-o funcție și blocul CATCH poate fi pus într-o altă funcție.

Nu vă repetați

Tot codul care este duplicat ar trebui extras într-o funcție separată. Nu doar veți reduce numărul de linii de cod, dar vă veți și ușura viața atunci când va fi nevoie să se facă o modificare. Este mai ușor să modifici numai o linie de cod decât să cauți și să modifici toate locațiile în care codul este duplicat.

Lucruri mărunte

După cum puteți vedea, lucrurile mărunte pot face o mare diferență între o funcție bună și una proastă. Nu este nevoie să faci sau să știi te miri ce nebunii pentru a fi capabil să scrii funcții "fericite". Ținând cont de aceste recomandări veți putea scrie un software mai bun, care peste 10 ani va fi întreținut mai ușor și cu mai puțini bani.

Sponsori

  • comply advantage
  • ntt data
  • 3PillarGlobal
  • Betfair
  • Telenav
  • Accenture
  • Siemens
  • Bosch
  • FlowTraders
  • MHP
  • Connatix
  • UIPatj
  • MetroSystems
  • Globant
  • Colors in projects