Refaktorovn Refactoring Dominik epk FJFI VUT 2019 Co

  • Slides: 30
Download presentation
Refaktorování (Refactoring) Dominik Šepák FJFI ČVUT 2019

Refaktorování (Refactoring) Dominik Šepák FJFI ČVUT 2019

Co je refactoring? Refactoring (noun): a change made to the internal structure of software

Co je refactoring? Refactoring (noun): a change made to the internal structure of software to make it easier to understand cheaper to modify without changing its observable behavior Zejména objektově orientované jazyky a non-real-time aplikace „Malá úprava, test, malá úprava, test, …“ Neupravujeme funkčnost, pouze tvar

Jak takový refactoring vypadá?

Jak takový refactoring vypadá?

Proč refaktorovat? Zlepšení designu programu Odstranění duplicitního kódu Lepší srozumitelnost kódu Celkové urychlení práce

Proč refaktorovat? Zlepšení designu programu Odstranění duplicitního kódu Lepší srozumitelnost kódu Celkové urychlení práce na projektu

Kdy refaktorovat? Nejlépe „za běhu“ a průběžně Cíl: ulehčit přidání další funkce do programu

Kdy refaktorovat? Nejlépe „za běhu“ a průběžně Cíl: ulehčit přidání další funkce do programu a. Pravidlo tří – implementovat, implementovat podruhé, refaktorovat b. Je těžké přidat novou funkčnost c. Musím opravit chybu

Kdy nerefaktorovat? Téměř neopravitelný kód Reraktorovat do několika funkčních celků -> ty pak ponechat

Kdy nerefaktorovat? Téměř neopravitelný kód Reraktorovat do několika funkčních celků -> ty pak ponechat či ne Před deadlinem

Refactoring a design kódu Nepotřebuji perfektní design – stačí dostatečně dobrý Zbytek obstará refaktoring

Refactoring a design kódu Nepotřebuji perfektní design – stačí dostatečně dobrý Zbytek obstará refaktoring

Refactoring a výkon Refactoring může program zpomalovat X Dobře čitelný a strukturovaný program se

Refactoring a výkon Refactoring může program zpomalovat X Dobře čitelný a strukturovaný program se mnohem lépe optimalizuje

Zápachy v kódu (v OOP) Konstrukce, které naznačují potřebu refactoringu Duplicitní kód Dlouhé metody

Zápachy v kódu (v OOP) Konstrukce, které naznačují potřebu refactoringu Duplicitní kód Dlouhé metody Dlouhé seznamy parametrů Nahradit objektem Metoda, která „vyjídá“ jinou třídu Přesunout/rozdělit

Zápachy v kódu (v OOP) Switch Využít polymorfismus (Téměř) prázdná třída Dlouhé řetězce volání

Zápachy v kódu (v OOP) Switch Využít polymorfismus (Téměř) prázdná třída Dlouhé řetězce volání (prostředník) Třídy, které slouží výhradně jako kontejner pro data Odstranit veřejný přístup k datům Pokusit se přenést metody Komentáře snažící se zamaskovat nesrozumitelnost kódu

Jednotkové testy (unit tests) Self-checking Plně automatické Význam: vyhnout se debugování Otestovat zejména extrémní

Jednotkové testy (unit tests) Self-checking Plně automatické Význam: vyhnout se debugování Otestovat zejména extrémní případy (netřeba otestovat úplně všechno)

Příklady Kompletní referenci lze nalézt v M. Fowler: Refactoring: Improving the Design of Existing

Příklady Kompletní referenci lze nalézt v M. Fowler: Refactoring: Improving the Design of Existing Code

Extract method Přemění část kódu na metodu Použití: a. dlouhé metody b. řešení nesrozumitelnosti

Extract method Přemění část kódu na metodu Použití: a. dlouhé metody b. řešení nesrozumitelnosti kódu 1. Vytvořit novou metodu 2. Zkopírovat kód 3. Nalézt lokální proměnné a v případě potřeby je předat jako parametr 4. Dočasné proměnné přesunout do nové metody, pokud to lze Vrátit modifikované proměnné (jako návratovou hodnotu) Pro více proměnných je potřeba metodu „rozřezat“ 5. Zkompilovat 6. Nahradit původní kód voláním metody, odstranit zbytečné dočasné proměnné 7. Zkompilovat a otestovat

Replace temp with query Přemění výraz na metodu a nahradí všechny výskyty dočasné proměnné

Replace temp with query Přemění výraz na metodu a nahradí všechny výskyty dočasné proměnné touto metodou Cíl: odstranění dočasné proměnné Je potřeba si dávat pozor, zda do proměnné nepřiřazuji více než jednou! 1. Nalézt lokální dočasnou proměnnou, do které se přiřazuje jednou 2. Deklarovat ji jako final 3. Zkompilovat Pokud bych do této proměnné ukládal vícekrát, způsobí to chybu 4. Změnit pravou stranu přiřazení na metodu 5. Zkompilovat, otestovat 6. Nahradit výskyty proměnné metodou, po každém nahrazení otestovat, následně vymazat dočasnou proměnnou

Move Method 1. Vytvoří novou metodu s podobným tělem v třídě, jejíž data daná

Move Method 1. Vytvoří novou metodu s podobným tělem v třídě, jejíž data daná metoda užívá nejvíce Prozkoumat prostředky využívané původní metodou a rozhodnout se, zda je také přesunout Více metod najednou se přesouvá snadněji 2. Ověřit předky a potomky na deklarace této metody (nechci porušit polymorfismus) 3. Deklarovat metodu ve výsledné třídě 4. Překopírovat zdrojový kód a upravit ho tak, aby fungoval a. Přesunout používané metody/atributy do cílové třídy b. Vytvořit či využít referenci na zdrojovou třídu v cílové třídě c. Předat zdrojový objekt jako parametr d. Předat proměnnou jako parametr

Move Method 5. Zkompilovat 6. Prohledat zdrojovou třídu a zjistit, zda se mohu odkazovat

Move Method 5. Zkompilovat 6. Prohledat zdrojovou třídu a zjistit, zda se mohu odkazovat na cílovou třídu, případně opravit (např. přidat atribut) 7. Přeměnit původní metodu na delegující metodu (pouze volá novou metodu) 8. Zkompilovat, otestovat 9. Rozhodnout o odstranění původní metody ANO: nahradit všechny výskyty novou metodou 10. Zkompilovat a otestovat

Encapsulate Field Změnit veřejný atribut na soukromý a přidat přístupové metody 1. Vytvořit getter

Encapsulate Field Změnit veřejný atribut na soukromý a přidat přístupové metody 1. Vytvořit getter a setter 2. Nalézt všechna použití atributu mimo třídu a nahradit getterem či setterem 3. Zkompilovat a otestovat po každé změně 4. Deklarovat atribut jako final 5. Zkompilovat a otetsovat

Move Field Atribut je/bude více využíván jinou třídou Přesune atribut do jiné třídy 1.

Move Field Atribut je/bude více využíván jinou třídou Přesune atribut do jiné třídy 1. Pokud je atribut veřejný, použít nejdřív „Encapsulate field“ 2. Zkompilovat, otestovat 3. Vytvořit atribut, setter a getter ve výsledné třídě 4. Zkompilovat 5. Promyslet, zda se jde odkazovat ze zdrojové třídy na cílovou, případně upravit kód tak, aby to šlo 6. Odstranit atribut ve zdrojové třídě 7. Zaměnit všechna použití 8. Zkompilovat, otestovat

Replace data value with object Mám atribut, co k sobě potřebuje „přibalit“ další atributy

Replace data value with object Mám atribut, co k sobě potřebuje „přibalit“ další atributy či metody Přemění atribut na objekt

Replace data value with object 1. Vytvořit třídu s daným atributem, deklarovat tento atribut

Replace data value with object 1. Vytvořit třídu s daným atributem, deklarovat tento atribut jako final, přidat getter a konstruktor 2. Zkompilovat 3. Změnit typ atributu ve zdrojové třídě na novou třídu 4. Změnit getter ve zdrojové třídě tak, aby volal getter nové třídy 5. Pokud je atribut zmíněn v konstruktoru zdrojové třídy, zavolat v tomto konstruktoru konstruktor nové třídy 6. Změnit setter tak, aby vytvářel novou instanci této třídy 7. Zkompilovat, otestovat

Replace magic number with symbolic constant double potential. Energy(double mass, double height) { return

Replace magic number with symbolic constant double potential. Energy(double mass, double height) { return mass * 9. 81 * height; } double potential. Energy(double mass, double height) { return mass * GRAVITATIONAL_CONSTANT * height; } static final double GRAVITATIONAL_CONSTANT = 9. 81;

Replace magic number with symbolic constant 1. Deklarovat konstantu a dát jí hodnotu nahrazovaného

Replace magic number with symbolic constant 1. Deklarovat konstantu a dát jí hodnotu nahrazovaného čísla 2. Najít všechny výskyty nahrazovaného čísla 3. Pokud se jedná opravdu o použití této konstanty, nahradit číslo konstantou Zkompilovat, otestovat

Decompose conditional Vytvoří z podmínky a bloků if-else metody Vhodné pro dlouhé nečitelné podmínky

Decompose conditional Vytvoří z podmínky a bloků if-else metody Vhodné pro dlouhé nečitelné podmínky v if-else if (date. before (SUMMER_START) || date. after(SUMMER_END)) charge = quantity * _winter. Rate + _ winter. Service. Charge; else charge = quantity * _summer. Rate; if (not. Summer(date)) charge = winter. Charge(quantity); else charge = summer. Charge (quantity);

Decompose conditional 1. Vytvořit z podmínky metodu 2. Vytvořit z bloků if-else metody 3.

Decompose conditional 1. Vytvořit z podmínky metodu 2. Vytvořit z bloků if-else metody 3. Zkompilovat, otestovat

Introduce assertion Určitá část kódu implicitně předpokládá něco o programu Mnohdy je tento požadavek

Introduce assertion Určitá část kódu implicitně předpokládá něco o programu Mnohdy je tento požadavek vyjádřen pouze v komentáři Učiní tento požadavek explicitní double get. Expense. Limit() { // should have either expense limit or a primary project return (_expense. Limit != NULL_EXPENSE) ? _expense. Limit: _primary. Project. get. Member. Expense. Limit(); } double get. Expense. Limit() { Assert. is. True(_expense. Limit != NULL_EXPENSE || _primary. Project !=null); return (_expense. Limit != NULL_EXPENSE) ? _expense. Limit: _primary. Project. get. Member. Expense. Limit(); }

Rename method Jméno metody nevypovídá o tom, co dělá Přejmenuje metodu Dnes obvykle součást

Rename method Jméno metody nevypovídá o tom, co dělá Přejmenuje metodu Dnes obvykle součást IDE 1. Vytvořit novou metodu stejné signatury a jiného jména, zkopírovat kód 2. Zkompilovat 3. Změnit tělo staré metody tak, aby volala novou 4. Zkompilovat, otestovat 5. Najít všechna použití staré metody, vždy změnit, zkompilovat, otestovat 6. Zkompilovat, otestovat

Preserve whole object Posílám velké množství informací z objektu jako parametr funkce Poslat celý

Preserve whole object Posílám velké množství informací z objektu jako parametr funkce Poslat celý objekt int low = days. Temp. Range(). get. Low(); int high = days. Temp. Range(). get. High(); within. Plan = plan. within. Range(low, high); within. Plan = plan. within. Range(days. Temp. Range());

Preserve whole object 1. Vytvořit nový parametr – objekt, co se bude předávat 2.

Preserve whole object 1. Vytvořit nový parametr – objekt, co se bude předávat 2. Zkompilovat, otestovat 3. Zjistit, které parametry používají daný objekt. 4. Nahradit jeden parametr voláním metod předávaného objektu 5. Odstranit tento parametr 6. Zkompilovat, otestovat 7. Zopakovat pro ostatní parametry

Děkuji za Vaši pozornost!

Děkuji za Vaši pozornost!

Zdroje FOWLER, Martin, et al. Refactoring: Improving the Design of Existing Code.

Zdroje FOWLER, Martin, et al. Refactoring: Improving the Design of Existing Code.