„Měl jsem bezesné noci, když jsem se snažil přidat funkce do kódu, který jsme získali od jiné společnosti. Mám co do činění s nejčistší formou Legacy Code“

„Mám opravdu potíže se zamotaným, nestrukturovaným kódem, se kterým musím pracovat, ale kterému ani trochu nerozumím. Legacy Code !“

Legacy Code je pojem, který má pravděpodobně mnoho různých definic jako -kód získaný od někoho jiného, kód napsaný někým jiným, kód, který je těžko pochopitelný nebo kód napsaný v zastaralých technologiích. Ať už je definice jakákoli, většina z nás se domnívá, že starší kód je děsivý.

Otázka> Jak byste definovali starší kód?

Definice staršího kódu

Michael Feathers ve své knize „Working Effectively with Legacy Code“ definuje starší kód jako, kód bez testů.

Kód bez testů je špatný kód. Nezáleží na tom, jak dobře je napsaný; jak dobře je strukturovaný; jak dobře je zapouzdřený. bez testů není možné zjistit, zda se náš kód zlepšuje nebo zhoršuje.

No, mírně upravená verze této definice zní „kód bez jednotkových testů se nazývá legacy kód“. Vždy je lepší mít testy co nejblíže kódu (unit testy > integrační testy > testy uživatelského rozhraní). Nebylo by tedy nespravedlivé nazývat kód bez jednotkových testů starším kódem.

Práce se starším kódem

Otázka> Jaký přístup zvolíte, pokud byste měli provést změnu ve starším kódu?“

Většina z nás si možná řekne: „Provedu změnu a tím to hasne, proč se zabývat vylepšováním kódu“. Odůvodnění tohoto myšlenkového postupu může být –

  • Nemám dost času na refaktorizaci kódu, raději provedu změnu a dokončím svůj příběh
  • Proč riskovat změnu struktury kódu, který již dlouho běží v produkci
  • Jaký je celkový přínos refaktorizace staršího kódu

Michael Feathers tento styl provádění změn nazývá Edit and Pray. Naplánujete a provedete změny, a když jste hotovi, modlíte se a modlíte se ještě usilovněji, aby vaše změny byly správné.

Tímto stylem lze pouze přispět ke zvýšení počtu starších kódů.

Existuje jiný styl provádění změn, kterým je Cover and Modify. Vytvořte bezpečnostní síť, proveďte změny v systému, nechte bezpečnostní síť poskytnout zpětnou vazbu a pracujte na těchto zpětných vazbách.

Dá se bezpečně předpokládat, že Cover and Modify je způsob, jak se vypořádat se starším kódem.

Otázka> Ale měli byste vůbec trávit čas psaním testů ve starším kódu nebo dokonce přemýšlet o refaktorizaci staršího kódu?

Měli byste vůbec trávit čas přemýšlením o refaktorizaci staršího kódu?

Skautské pravidlo

Myšlenka skautského pravidla, jak ji uvedl strýček Bob, je poměrně jednoduchá: Zanechte kód čistší, než jste ho našli! Kdykoli se dotknete starého kódu, měli byste jej řádně vyčistit. Nepoužívejte jen zkratkovité řešení, které ztíží pochopení kódu, ale zacházejte s ním pečlivě. Nestačí kód napsat dobře, kód je třeba udržovat v průběhu času čistý.

Při aplikaci skautského pravidla na starší kód dostaneme velmi silnou zprávu: „Zanechte za sebou stopu porozumění, kterou budou ostatní následovat.“ To znamená, že kód refaktorujeme, aby byl srozumitelnější. A abychom mohli refaktorovat, vybudujeme kolem něj bezpečnostní síť.

Teď, když chápeme, že nemůžeme jít zkratkou, zbývá nám jediná možnost: napsat několik testů, refaktorovat kód a pokračovat ve vývoji. Otázky>

  • Které testy bychom měli napsat?
  • Jak moc bychom měli refaktorovat?

Které testy napsat

Téměř u každého staršího systému je důležitější, co systém dělá, než co má dělat.

Testy charakterizace, testy, které potřebujeme, když chceme zachovat chování, se nazývají jako charakterizační testy. Charakterizační test je test, který charakterizuje skutečné chování části kódu. Neexistuje žádné „no, mělo by to dělat tohle“ nebo „myslím, že to dělá tohle“. Testy dokumentují skutečné aktuální chování systému.

Psaní charakterizačního testu

Karakterizační test z definice dokumentuje skutečné aktuální chování systému přesně tak, jak běží v produkčním prostředí.

Napíšeme charakterizační test pro objekt Customer, který generuje textový výpis pro některé filmy zapůjčené zákazníkem.

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);
}

Tento test se pokouší pochopit (neboli charakterizovat) generování „textového výpisu“ pro zákazníka, kterému je zadán dětský film zapůjčený na 3 dny. Protože systému nerozumíme (alespoň prozatím), očekáváme, že výpis bude prázdný nebo bude obsahovat nějakou fiktivní hodnotu.

Pustíme test a necháme ho selhat. Když se tak stane, zjistili jsme, co kód za této podmínky vlastně dělá.

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

Teď, když známe chování kódu, můžeme přistoupit ke změně testu.

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);
}

Podržte, zkopírovali jsme právě výstup vygenerovaný kódem a umístěný do našeho testu. Ano, přesně to jsme udělali.

Nesnažíme se teď hledat chyby. Snažíme se vložit mechanismus pro pozdější hledání chyb, chyb, které se projeví jako odlišnosti od současného chování systému. Když přijmeme tuto perspektivu, náš pohled na testy je jiný: Nemají žádnou morální autoritu; jen sedí a dokumentují, co systém skutečně dělá. V této fázi je velmi důležité mít někde tuto znalost toho, co systém skutečně dělá.

Otázka> Jaký je celkový počet testů, které napíšeme, abychom charakterizovali systém?

Odpověď> Je to nekonečné. Psaní jednoho případu za druhým pro libovolnou třídu ve starším kódu bychom mohli věnovat značnou část života.

Otázka> Kdy tedy přestaneme? Lze nějak zjistit, které případy jsou důležitější než jiné?“

Odpověď> Podívejte se na kód, který charakterizujeme. Kód sám o sobě nám může dát představu o tom, co dělá, a pokud máme otázky, testy jsou ideálním způsobem, jak je položit. V tu chvíli napište test nebo testy, které pokryjí dostatečně dobrou část kódu.

Otázka> Pokrývá to všechno v kódu?“

Odpověď> Nemusí. Ale pak uděláme další krok. Přemýšlíme o změnách, které chceme v kódu provést, a snažíme se zjistit, zda testy, které máme, vycítí nějaké problémy, které můžeme způsobit. Pokud ne, přidáme další testy, dokud nebudeme mít jistotu, že ano.

Kolik toho refaktorizovat?

Ve starším kódu je toho tolik k refaktorizaci a nemůžeme refaktorizovat všechno. Abychom na to mohli odpovědět, musíme se vrátit k pochopení účelu refaktorizace staršího kódu.

Chceme refaktorovat starší kód tak, aby zůstal čistší, než jaký byl, když k nám přišel, a aby byl srozumitelný pro ostatní.

S tím, co bylo řečeno, chceme systém vylepšit a přitom se soustředit na úkol. Nechceme se zbláznit z refaktoringu a snažit se přepsat celý systém během několika dní. To, co chceme udělat, je „refaktorovat kód, který nám stojí v cestě při implementaci jakékoli nové změny“. V příštím článku se to pokusíme lépe pochopit na příkladu.

Závěr

.

Articles

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.