Možná píšu o něčem, co je pro mnohé zřejmé, ale možná ne pro všechny. Refaktoring je podle mě složité téma, protože zahrnuje změnu kódu bez vlivu na jeho fungování.
Proto je pro některé nepochopitelné, že refaktoring je vlastně oblast programování a je také velmi důležitou součástí práce programátora. Kód se neustále vyvíjí, bude se upravovat, dokud bude existovat možnost přidávání nových funkcí. Může však nabýt takové podoby, že již neumožňuje efektivní přidávání nových funkcí a bylo by jednodušší celý program přepsat.
Co je refaktoring?
Obvykle se setkáte s odpovědí, že se jedná o změnu struktury kódu pomocí řady refaktorizačních transformací, aniž by bylo ovlivněno pozorovatelné chování kódu. To je pravda. Nedávno jsem také narazil na definici podle Martin Fowler ve své knize "Zlepšení návrhu stávajícího kodexu" kde popisuje refaktoring jako "provádění velkých změn po malých krocích". Popisuje refaktoring jako změnu kódu, která nemá vliv na jeho fungování, ale zdůrazňuje, že je třeba ji provádět po malých krocích.
V knize se také uvádí, že refaktoring neovlivňuje fungování kódu a upozorňuje, že nemá žádný vliv na úspěšné absolvování testů. Popisuje krok za krokem, jak bezpečně provést refaktoring. Jeho kniha se mi líbila, protože popisuje jednoduché triky, které lze využít při každodenní práci.
Proč potřebujeme refaktoring?
Nejčastěji ji můžete potřebovat, když chcete zavést novou funkci a kód ve své současné verzi to neumožňuje nebo by to bylo obtížnější beze změn v kódu. Hodí se také v případech, kdy je přidávání dalších funkcí časově nevýhodné, tj. bylo by rychlejší kód přepsat od začátku. Myslím, že se někdy zapomíná, že refaktoring může být kód čistší a čitelnější. Martin ve své knize píše, jak provádí refaktoring, když cítí v kódu nepříjemný zápach, a jak to říká, "vždycky nechává prostor pro to lepší". A překvapil mě tím, že refaktoring považuje za součást každodenní práce s kódem. Pro mě jsou kódy často nesrozumitelné, číst je je trochu zážitek, protože kód je často neintuitivní.
Charakteristickým rysem dobře navrženého programu je jeho modularita, díky čemuž stačí znát jen malou část kódu, aby bylo možné zavést většinu úprav. Modularita také usnadňuje nástup nových lidí a umožňuje jim efektivněji začít pracovat. Aby bylo možné této modularity dosáhnout, musí být příbuzné programové prvky seskupeny dohromady, přičemž souvislosti musí být srozumitelné a snadno nalezitelné. Neexistuje žádné jednotné pravidlo, jak toho dosáhnout. Jakmile poznáte a pochopíte více a lépe, jak má kód fungovat, můžete prvky seskupovat, ale někdy je také musíte testovat a kontrolovat.
Jedním z pravidel refaktoringu v YAGNI, což je zkratka pro "You Aren't Gonna Need It" (Nebudeš to potřebovat) a pochází z anglického názvu eXtreme Programming (XP) používá se zejména v Agilnívývoj softwaru týmy. Zkrátka a dobře, YAGNI říká, že by se měly provádět pouze aktuální věci. To v podstatě znamená, že i když by něco mohlo být v budoucnu potřeba, nemělo by se to dělat hned. Ale také nemůžeme drtit další rozšíření a zde se stává důležitou modularita.
Když mluvíme o refaktoring, je třeba zmínit jeden z nejpodstatnějších prvků, tj. testy. Na stránkách refaktoring, potřebujeme vědět, že kód stále funguje, protože refaktoring nemění způsob fungování, ale jeho strukturu, takže všechny testy musí být splněny. Nejlepší je po každé malé transformaci spustit testy pro část kódu, na které pracujeme. To dává nás potvrzení, že vše funguje, jak má, a zkracuje dobu celé operace. Právě o tom mluví Martin ve své knize - provádějte testy co nejčastěji, abyste nemuseli dělat krok zpět a ztrácet čas hledáním transformace, která něco rozbila.
Refaktorizace kódu bez testování je to otrava a je velká šance, že se něco pokazí. Pokud je to možné, bylo by nejlepší přidat alespoň základní testy, které nám dají trochu jistoty, že kód funguje.
Níže uvedené transformace jsou pouze příklady, ale jsou opravdu užitečné při každodenním programování:
Extrakce funkcí a proměnných - pokud je funkce příliš dlouhá, zkontrolujte, zda existují nějaké menší funkce, které by bylo možné extrahovat. Totéž platí pro dlouhé řádky. Tyto transformace mohou pomoci při hledání duplicit v kódu. Díky Malým funkcím se kód stává přehlednějším a srozumitelnějším,
Přejmenování funkcí a proměnných - pro správné programování je důležité používat správné pojmenování. Vhodně zvolené názvy proměnných mohou o kódu hodně napovědět,
Seskupení funkcí do jedné třídy - tato změna je užitečná, pokud dvě třídy provádějí podobné operace, protože může zkrátit délku třídy,
Překrývání vnořeného příkazu - pokud podmínka vyhovuje pro zvláštní případ, vydejte příkaz return, když podmínka nastane. Tyto typy testů se často označují jako ochranná klauzule. Nahrazení vnořeného podmíněného příkazu příkazem ukončení mění důraz v kódu. Konstrukce if-else přiřazuje oběma variantám stejnou váhu. Pro toho, kdo kód čte, je to zpráva, že každá z nich je stejně pravděpodobná a důležitá,
Zavedení zvláštního případu - pokud některé podmínky používáte ve svém kódu mnohokrát, možná by stálo za to vytvořit pro ně samostatnou strukturu. Díky tomu lze většinu kontrol zvláštních případů nahradit jednoduchým voláním funkcí. Častou hodnotou, která vyžaduje zvláštní zpracování, je často null. Proto se tento vzor často nazývá nulový objekt. Tento přístup však lze použít v jakémkoli zvláštním případě,
Nahrazení podmíněného polymorfismu instrukcí.
Příklad
Toto je článek o refaktoring a je třeba uvést příklad. Níže chci ukázat jednoduchou ukázku refaktorizace s použitím Přepsání vnořeného příkazu a Nahrazení podmíněného polymorfismu instrukcí. Řekněme, že máme programovou funkci, která vrací hash s informacemi o tom, jak zalévat rostliny v reálném životě. Taková informace by pravděpodobně byla v modelu, ale pro tento příklad ji máme ve funkci.
def zavlažování_info(plant)
result = {}
if plant.is_a? Suculent || plant.is_a? Cactus
result = { water_amount: "Trochu " , how_to: "Ze dna", zalévání_délka: "2 týdny" }
elsif plant.is_a? Alocasia || plant.is_a? Maranta
result = { water_amount: "Velké množství", how_to: "Jak si přejete", watering_duration: "5 dní" }
elsif plant.is_a? Peperomia
result = { water_amount: "Dicent amount",
how_to: "nemají rády vodu na listech",
doba zalévání: "1 týden" }
else
result = { water_amount: "Dicent amount",
how_to: "Jak si přejete",
doba trvání zavlažování: "1 týden"
}
end
return result
konec
Jde o to, aby se změnila, pokud se vrátí:
if plant.isa? Suculent || plant.isa? Cactus
result = { wateramount: "A little bit " , howto: "Ze dna",
A tak dále se vším všudy, až dojdeme k funkci, která vypadá takto:
def zavlažování_info(plant)
return result = { wateramount: "a little bit " , howto: "Ze dna", wateringduration: "2 týdny" } if plant.isa? Suculent || plant.is_a? Cactus
return result = { wateramount: "Velké množství", howto: "Jak si přejete", wateringduration: "5 dní" } if plant.isa? Alocasia || plant.is_a? Maranta
return result = { water_amount: "Dicent amount",
howto: "Odspodu! nemají rády vodu na listech",
wateringduration: "1 týden" } if plant.is_a? Peperomia
return result = { water_amount: "Dicent amount",
how_to: "Jak si přejete",
zalévání_duration: "1 týden"
}
end
Na samém konci jsme již měli výsledek. A dobrým zvykem je postupovat krok za krokem a každou změnu otestovat. Tento blok if byste mohli nahradit případem switch a hned by to vypadalo lépe a nemuseli byste pokaždé kontrolovat všechna if. Vypadalo by to takto:
def zavlažování_info(plant)
swich plant.class.to_string
case sukulent, kaktus
{ wateramount: "A little bit " , howto: "Ze dna", zalévání_duration: "2 týdny" }
case Alocasia, Maranta
{ wateramount: "Velké množství", howto: Délka zalévání: "Jak si přejete", watering_duration: "5 dní" }
case Peperomia
{ water_amount: "Dicent amount",
how_to: "nemají rády vodu na listech",
doba zalévání: "1 týden" }
jinak
{ water_amount: "Dicent amount",
how_to: "Jak si přejete",
zalévání_duration: "1 týden" }
end
end
A pak můžete použít Nahrazení podmíněného polymorfismu instrukcí. To znamená vytvořit třídu s funkcí, která vrátí správnou hodnotu a přepne je na správné místo.
třída Suculent
...
def zalévání_info()
return { wateramount: "Trochu " , howto: "Ze dna", watering_duration: "2 týdny" }
end
end
třída Cactus
...
def zalévání_info()
return { wateramount: "Trochu " , howto: "Ze dna", watering_duration: "2 týdny" }
end
end
třída Alocasia
...
def zavlažování_info
return { wateramount: "Velké množství", howto: "Jak si přejete", watering_duration: "5 dní" }
end
end
třída Maranta
...
def zavlažování_info
return { wateramount: "Velké množství", howto: "Jak si přejete", watering_duration: "5 dní" }
end
end
třída Peperomia
...
def zavlažování_info
return { water_amount: "Dicent amount",
how_to: "nemají rádi vodu na listech",
doba zalévání: "1 týden" }
end
end
třída Plant
...
def zavlažování_info
return { water_amount: "Dicent amount",
how_to: "Jak si přejete",
zalévání_duration: "1 týden" }
end
end
A v hlavní funkci zavlažování_infofunkce bude kód vypadat takto:
def zavlažování_info(plant)
plant.map(&:watering_info)
konec
Tuto funkci lze samozřejmě odstranit a nahradit jejím obsahem. Tímto příkladem jsem chtěl představit obecný princip. vzor refaktoringu.
Souhrn
Refaktoring je velkým tématem. Doufám, že tento článek byl podnětem k dalšímu čtení. Tyto stránky dovednosti refaktoringu vám pomůže zachytit chyby a zlepšit vaši dílnu čistého kódu. Doporučuji přečíst si Martinovu knihu (Improving the Design of Existing Code), která je docela základním a užitečným souborem pravidel refaktoring. Autor ukazuje různé transformace krok za krokem s úplným vysvětlením a motivací a radami, jak se vyhnout chybám při transformaci. refaktoring. Díky své všestrannosti je to úžasná kniha pro frontend a vývojáři backendu.