„Am avut nopți nedormite încercând să adaug caracteristici în codul pe care l-am achiziționat de la altă companie. Am de-a face cu cea mai pură formă de Legacy Code”

„Îmi este foarte greu să mă descurc cu codul încâlcit și nestructurat cu care trebuie să lucrez, dar pe care nu-l înțeleg deloc. Legacy Code !”

Legacy Code este un termen care, probabil, are o mulțime de definiții diferite, cum ar fi -cod achiziționat de la altcineva, cod scris de altcineva, cod care este greu de înțeles sau cod scris în tehnologii învechite. Oricare ar fi definiția, cei mai mulți dintre noi credem că codul moștenit este înfricoșător.

Întrebare> Cum ați defini codul moștenit?

Definirea codului moștenit

Michael Feathers în cartea sa „Working Effectively with Legacy Code” definește codul moștenit ca, cod fără teste.

Codul fără teste este un cod prost. Nu contează cât de bine este scris; cât de bine este structurat; cât de bine este încapsulat. fără teste nu există nicio modalitate de a spune dacă codul nostru se îmbunătățește sau se înrăutățește.

Bine, o versiune ușor modificată a acestei definiții este „codul fără teste de unitate se numește cod moștenit”. Este întotdeauna mai bine să avem teste cât mai aproape de cod (teste unitare > teste de integrare > teste UI). Așadar, nu ar fi nedrept să numim un cod fără teste unitare cod moștenit.

Lucrul cu codul moștenit

Întrebare> Ce abordare veți adopta dacă ar trebui să faceți o modificare în codul moștenit?

Majoritatea dintre noi ar putea spune: „Voi face modificarea și o voi numi o zi, de ce să ne mai chinuim să îmbunătățim codul”. Raționamentul din spatele acestui proces de gândire ar putea fi –

  • Nu am suficient timp pentru a refactoriza codul, aș prefera să fac o modificare și să-mi finalizez povestea
  • De ce să risc să schimb structura codului care rulează în producție de mult timp
  • Care este beneficiul general al refactorizării codului moștenit

Michael Feathers numește acest stil de a face o modificare „Edit and Pray”. Planificați și faceți modificările, iar când ați terminat, vă rugați și vă rugați și mai mult pentru ca modificările să fie corecte.

Cu acest stil, nu se poate contribui decât la creșterea codului moștenit.

Există un stil diferit de a face modificări care este Cover and Modify (Acoperă și Modifică). Construiți o plasă de siguranță, faceți modificări în sistem, lăsați plasa de siguranță să ofere feedback și lucrați pe baza acestor feedback-uri.

Se poate presupune în mod sigur că Cover and Modify este o cale de urmat pentru a face față codului moștenit.

Întrebare> Dar, ar trebui chiar să vă petreceți timpul scriind teste în codul moștenit sau chiar să vă gândiți la refactorizarea unui cod moștenit?

Ar trebui măcar să vă petreceți timpul gândindu-vă la refactorizarea codului moștenit ?

Regula cercetașilor

Ideea din spatele regulii cercetașilor, așa cum a fost enunțată de unchiul Bob, este destul de simplă: Lasă codul mai curat decât l-ai găsit! Ori de câte ori atingeți un cod vechi, ar trebui să îl curățați corespunzător. Nu aplicați doar o soluție de scurtătură care va face codul mai greu de înțeles, ci tratați-l cu grijă. Nu este suficient să scrii bine codul, codul trebuie să fie păstrat curat în timp.

Avem un mesaj foarte puternic atunci când regula cercetașilor este aplicată codului vechi „lasă o urmă de înțelegere în urma ta pentru ca alții să te urmeze”, ceea ce înseamnă că vom refactoriza codul pentru a-l face mai ușor de înțeles. Și pentru a refactoriza, vom construi Safety Net în jurul acestuia.

Acum că am înțeles că nu putem lua scurtături, singura opțiune care ne rămâne este să scriem niște teste, să refactorizăm codul și să continuăm dezvoltarea. Întrebări>

  • Ce teste ar trebui să scriem?
  • Cât de mult ar trebui să refactorizăm?

Ce teste să scriem

În aproape toate sistemele moștenite, ceea ce face sistemul este mai important decât ceea ce ar trebui să facă.

Testele de caracterizare, testele de care avem nevoie atunci când dorim să păstrăm comportamentul se numesc teste de caracterizare. Un test de caracterizare este un test care caracterizează comportamentul real al unei bucăți de cod. Nu există „Ei bine, ar trebui să facă asta” sau „Cred că face asta”. Testele documentează comportamentul curent real al sistemului.

Scrierea testului de caracterizare

Un test de caracterizare documentează prin definiție comportamentul curent real al sistemului exact în același mod în care rulează în mediul de producție.

Să scriem un test de caracterizare pentru un obiect Client care generează o declarație de text pentru unele filme închiriate de un client.

import static com.code.legacy.movie.MovieType.CHILDREN;
import static org.junit.Assert.assertEquals;public void shouldGenerateTextStatement(){ Customer john = new Customer("John");
Movie childrenMovie = new Movie("Toy Story", CHILDREN);
int daysRented = 3;
Rental rental = new Rental(childrenMovie, daysRented); john.addRental(rental); String statement = john.generateTextStatement();
assertEquals("", statement);
}

Acest test încearcă să înțeleagă (sau să caracterizeze) generarea „Declarației de text” pentru un client dat un film pentru copii închiriat pentru 3 zile. Deoarece nu înțelegem sistemul (cel puțin deocamdată), ne așteptăm ca declarația să fie goală sau să conțină orice valoare fictivă.

Să executăm testul și să îl lăsăm să eșueze. Când o face, am aflat ce face de fapt codul în acea condiție.

java.lang.AssertionError: 
Expected :""
Actual :Rental Record for John, Total amount owed = 12.5. You earned 4 frequent renter points.

Acum, că știm comportamentul codului, putem merge mai departe și să modificăm testul.

import static com.code.legacy.movie.MovieType.CHILDREN;
import static org.junit.Assert.assertEquals;public void shouldGenerateTextStatement(){
String expectedStatement = "Rental Record for John, Total amount owed = 12.5. You earned 4 frequent renter points"; Customer john = new Customer("John");
Movie childrenMovie = new Movie("Toy Story", CHILDREN);
int daysRented = 3;
Rental rental = new Rental(childrenMovie, daysRented);
john.addRental(rental); Sting statement = john.generateTextStatement();
assertEquals(expectedStatement, statement);
}

Așteptați, tocmai am copiat ieșirea generată de cod și plasată în testul nostru. Da, exact asta am făcut.

Nu încercăm să găsim bug-uri chiar acum. Încercăm să punem un mecanism pentru a găsi bug-uri mai târziu, bug-uri care apar ca diferențe față de comportamentul actual al sistemului. Când adoptăm această perspectivă, viziunea noastră asupra testelor este diferită: ele nu au nicio autoritate morală; ele doar stau acolo și documentează ceea ce face cu adevărat sistemul. În acest stadiu, este foarte important să avem undeva această cunoaștere a ceea ce face de fapt sistemul.

Întrebare> Care este numărul total de teste pe care le scriem pentru a caracteriza un sistem?

Răspuns> Este infinit. Am putea dedica o bună parte din viața noastră pentru a scrie caz după caz pentru orice clasă dintr-un cod moștenit.

Întrebare> Când ne oprim atunci? Există vreo modalitate de a ști care cazuri sunt mai importante decât altele?

Răspuns> Priviți codul pe care îl caracterizăm. Codul în sine ne poate da idei despre ceea ce face, iar dacă avem întrebări, testele sunt o modalitate ideală de a le pune. În acel moment, scrieți unul sau mai multe teste care să acopere o porțiune suficient de bună din cod.

Întrebare> Acoperă asta tot codul?

Răspuns> S-ar putea să nu. Dar atunci facem următorul pas. Ne gândim la schimbările pe care vrem să le facem în cod și încercăm să ne dăm seama dacă testele pe care le avem vor sesiza orice problemă pe care o putem cauza. Dacă nu o vor face, adăugăm mai multe teste până când avem încredere că o vor face.

Cât de mult să refactorizăm?

Există atât de multe de refactorizat în codul moștenit și nu putem refactoriza totul. Pentru a răspunde la această întrebare, trebuie să ne întoarcem la înțelegerea scopului nostru de refactorizare a codului moștenit.

Vrem să refactorizăm codul moștenit pentru a-l lăsa mai curat decât era când a venit la noi și pentru a-l face ușor de înțeles pentru alții.

După aceasta, vrem să facem sistemul mai bun, menținând concentrarea pe sarcină. Nu vrem să o luăm razna cu refactorizarea încercând să rescriem întregul sistem în câteva zile. Ceea ce vrem să facem este să „refactorizăm codul care ne stă în calea implementării oricărei modificări noi”. Vom încerca să înțelegem mai bine acest lucru cu un exemplu în articolul următor.

Concluzie

.

Articles

Lasă un răspuns

Adresa ta de email nu va fi publicată.