”Minulla on ollut unettomia öitä, kun olen yrittänyt lisätä ominaisuuksia koodiin, jonka olemme hankkineet toiselta yritykseltä. Olen tekemisissä perintökoodin puhtaimman muodon kanssa”
”Minulla on todella vaikeaa käsitellä sekavaa, jäsentymätöntä koodia, jonka kanssa minun on työskenneltävä, mutta jota en ymmärrä yhtään. Legacy Code !”
Legacy code on termi, jolla on luultavasti paljon erilaisia määritelmiä, kuten -joltain toiselta hankittu koodi, jonkun toisen kirjoittama koodi, vaikeasti ymmärrettävä koodi tai vanhentuneilla teknologioilla kirjoitettu koodi. Olipa määritelmä mikä tahansa, useimmat meistä uskovat, että perintökoodi on pelottavaa.
Kysymys> Miten sinä määrittelisit perintökoodin?
Perintökoodin määrittely
Michael Feathers kirjassaan ”Working Effectively with Legacy Code” (Tehokasta työskentelyä perintökoodin kanssa) määrittelee perintökoodin seuraavasti: koodi, jossa ei ole testejä.
Koodi, jossa ei käytetä testejä, on huono koodi. Sillä ei ole väliä kuinka hyvin kirjoitettu se on; kuinka hyvin jäsennelty se on; kuinka hyvin kapseloitu se on. ilman testejä ei ole mitään keinoa kertoa, paraneeko vai huononeeko koodimme.
No, hieman muunneltu versio tästä määritelmästä on ”koodia, jossa ei ole yksikkötestejä, kutsutaan perintökoodiksi”. On aina parempi, että testit ovat mahdollisimman lähellä koodia (yksikkötestit > integraatiotestit > UI-testit). Ei siis olisi epäreilua kutsua koodia, jossa ei ole yksikkötestejä, perintökoodiksi.
Työskentely perintökoodin kanssa
Kysymys> Minkälaisen lähestymistavan ottaisit käyttöön, jos tekisit muutoksen perintökoodiin?
Vähemmistö meistä saattaisi sanoa, että ”teen muutoksen ja jätän asian sikseen, miksi vaivautua parantamaan koodia”. Perusteluna tälle ajattelutavalle voi olla –
- Minulla ei ole tarpeeksi aikaa refaktoroida koodia, Haluaisin mieluummin tehdä muutoksen ja saada tarinani valmiiksi
- Miksi ottaa riski muuttaa sellaisen koodin rakennetta, joka on toiminut tuotannossa jo pitkään
- Mitä kokonaishyötyä perintökoodin refaktoroinnista on
Michael Feathers kutsuu tätä muutostyyliä nimellä Edit and Pray. Suunnittelet ja teet muutokset, ja kun olet valmis, rukoilet ja rukoilet vielä enemmän saadaksesi muutokset kuntoon.
Tällä tyylillä voi vain edesauttaa Legacy-koodin kasvattamista.
>
Muutosten tekemisessä on erilainen tyyli, joka on Cover and Modify (peitä ja muuta). Rakennetaan turvaverkko, tehdään muutoksia järjestelmään, annetaan turvaverkon antaa palautetta ja työstetään näitä palautteita.
Voidaan turvallisesti olettaa, että Cover and Modify on oikea tapa käsitellä Legacy-koodia.
Kysymys> Mutta pitäisikö edes käyttää aikaa testien kirjoittamiseen legacy-koodiin tai edes miettiä legacy-koodin refaktorointia?
Poikapartiosääntö
Poikapartiosäännön taustalla oleva ajatus, sellaisena kuin se on sanottuna Bob-sedän sanomana, on melko yksinkertainen: Jätä koodi puhtaampana kuin löysit sen! Aina kun kosketat vanhaa koodia, puhdista se kunnolla. Älä vain sovella oikotietoratkaisua, joka tekee koodista vaikeammin ymmärrettävää, vaan käsittele sitä huolellisesti. Ei riitä, että kirjoitat koodia hyvin, vaan koodi on pidettävä puhtaana ajan mittaan.
Saatamme erittäin vahvan viestin, kun partiopoikien sääntöä sovelletaan vanhaan koodiin ”jätä jälkeesi ymmärtämisen jälki, jota muut voivat seurata”, mikä tarkoittaa, että refaktoroimme koodia, jotta siitä tulisi ymmärrettävämpää. Ja refaktoroidaksemme rakennamme Safety Netin sen ympärille.
Nyt kun ymmärrämme, ettemme voi käyttää oikoteitä, ainoa vaihtoehto, joka meille jää, on kirjoittaa joitakin testejä, refaktoroida koodi ja jatkaa kehitystä. Kysymyksiä>
- Mitä testejä meidän pitäisi kirjoittaa?
- Kuinka paljon meidän pitäisi refaktoroida?
Mitä testejä kannattaa kirjoittaa
Lähes jokaisessa legacy-järjestelmässä se, mitä järjestelmä tekee, on tärkeämpää kuin se, mitä sen pitäisi tehdä.
Charakterisointitestejä, testejä, joita tarvitsemme, kun haluamme säilyttää käyttäytymisen, kutsutaan karakterisointitesteiksi. Luonnehdintatesti on testi, joka luonnehtii koodin todellista käyttäytymistä. Ei ole mitään ”No, sen pitäisi tehdä näin” tai ”Luulen, että se tekee noin”. Testit dokumentoivat järjestelmän todellisen nykyisen käyttäytymisen.
Karakterisointitestin kirjoittaminen
Karakterisointitesti dokumentoi määritelmän mukaan järjestelmän todellisen nykyisen käyttäytymisen täsmälleen samalla tavalla kuin se toimii tuotantoympäristössä.
Kirjoitetaan karakterisointitesti Asiakas-objektille, joka tuottaa tekstilausuman joillekin asiakkaan vuokraamille elokuville.
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);
}
Tässä testissä yritetään ymmärtää (tai karakterisoida) ”Tekstilausuman” tuottamista asiakkaalle, jolle on annettu 3 päiväksi vuokrattu lastenelokuva. Koska emme ymmärrä järjestelmää (ainakaan toistaiseksi), odotamme lausuman olevan tyhjä tai sisältävän minkä tahansa dummy-arvon.
Ajetaan testi ja annetaan sen epäonnistua. Kun se epäonnistuu, olemme saaneet selville, mitä koodi todellisuudessa tekee kyseisessä tilanteessa.
java.lang.AssertionError:
Expected :""
Actual :Rental Record for John, Total amount owed = 12.5. You earned 4 frequent renter points.
Nyt, kun tiedämme koodin käyttäytymisen, voimme mennä eteenpäin ja muuttaa testiä.
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);
}
Odota, kopioimmeko juuri koodin tuottaman tulosteen ja sijoitimme sen testiimme. Kyllä, juuri niin teimme.
Me emme yritä löytää vikoja juuri nyt. Yritämme laittaa mekanismin, jolla löydämme virheitä myöhemmin, virheitä, jotka näkyvät eroavaisuuksina järjestelmän nykyisestä käyttäytymisestä. Kun omaksumme tämän näkökulman, näkemyksemme testeistä on erilainen: niillä ei ole mitään moraalista auktoriteettia; ne vain istuvat dokumentoimassa, mitä järjestelmä todella tekee. Tässä vaiheessa on hyvin tärkeää, että tieto siitä, mitä järjestelmä oikeasti tekee, on jossakin.
Kysymys> Mikä on niiden testien kokonaismäärä, jotka kirjoitamme järjestelmän kuvaamiseksi?
Vastaus> Se on ääretön. Voisimme omistaa hyvän osan elämästämme sille, että kirjoitamme tapauksen toisensa jälkeen mille tahansa perintökoodin luokalle.
Kysymys> Milloin me sitten lopetamme? Voimmeko mitenkään tietää, mitkä tapaukset ovat tärkeämpiä kuin toiset?
Vastaus> Katso koodia, jota luonnehdimme. Koodi itsessään voi antaa meille ideoita siitä, mitä se tekee, ja jos meillä on kysymyksiä, testit ovat ihanteellinen tapa kysyä niitä. Kirjoita siinä vaiheessa testi tai testit, jotka kattavat riittävän hyvän osan koodista.
Kysymys> Kattaako se kaiken koodissa?
Vastaus> Ei välttämättä. Mutta sitten teemme seuraavan vaiheen. Ajattelemme muutoksia, joita haluamme tehdä koodiin, ja yritämme selvittää, aistivatko testit, joita meillä on, mitään ongelmia, joita voimme aiheuttaa. Jos ne eivät havaitse, lisäämme lisää testejä, kunnes olemme varmoja, että ne havaitsevat.
Miten paljon refaktoroida?
Legacy-koodissa on niin paljon refaktoroitavaa, emmekä voi refaktoroida kaikkea. Vastataksemme tähän meidän on palattava ymmärtämään, mikä on perintökoodin refaktoroinnin tarkoituksemme.
Haluamme refaktoroida perintökoodia, jotta se jäisi siistimmäksi kuin mitä se oli meille tullessaan ja jotta se olisi ymmärrettävää muille.
Tämän sanottuamme haluamme tehdä järjestelmästä paremman pitäen fokuksen tehtävässä. Emme halua sekoilla refaktoroinnissa yrittäen kirjoittaa koko järjestelmää uudelleen muutamassa päivässä. Haluamme vain ”refaktoroida koodin, joka on tiellämme uusien muutosten toteuttamisessa”. Yritämme ymmärtää tätä paremmin esimerkin avulla seuraavassa artikkelissa.