REFAKTORING [1] Návrhové vzory predstavujú podľa Gammu ciele tvorby (kvalitný kód), refaktoring samotný je postup, ako toho dosiahnuť. Refaktoring ako súčasť Extreme Programming, je transformácia funkčného kódu do efektívnejšieho tvaru: zlepšujete návrh kódu potom, čo bol napísaný [1]. Nie dobrý návrh a potom implementácia (jeho zchátranie, hnitie a hackovanie, vytrácanie sa pôvodného návrhu), ale zlý kód (až chaos) a jeho postupné zlepšovanie v primitívnych krokoch za neustáleho fungovania aplikácie. Okrem zjemňovania dedenia a odstraňovania nadbytočných riadkov, objavujeme stále ďalšie a ďalšie príležitosti zefektívnenia a prehľadávame kód stále hlbšie a hlbšie. Tento riskantný spôsob zlepšovania kódu musí byť disciplinovaný, aby bolo zabezpečená neustála funkčnosť systému. Vytvorenie hierarchie v návrhu a dopĺňanie ďalších tried vedie k nadbytočnému kódu, pretože prehistorické predpoklady nadtried nie sú splnené v nových podtriedach a je potrebné prekrytie metód a podobne. Refaktorovanie je potrebné, ak nie je možné jednoducho pridať ďalšiu funkcionalitu systému. s.78, s.84 Začiatkom refaktoringu je zavedenie automatických testov. Refaktorovanie sa vedie po malých krokoch pri nesprávnom zásahu je jednoduché krok napraviť. Princíp dvoch klobúkov podľa Kenta Becka: v jednom pridávame funkcionalitu a nemeníme štruktúru, v druhom meníme štruktúru a nepridávame žiadnu funkcionalitu. Môžeme ich meniť aj behom piatich minút pri novej funkcii, ale vždy musíme vedieť, čo robíme (aký klobúk máme na hlave) a nemiešať pridávanie s refaktoringom. Prečo refaktoring: 1. zlepšuje návrh softwaru 2. zlepšuje jeho čitateľnosť 3. umožňuje nájsť chyby pri zmene Kedy je potrebné refaktorovať: 1. keď aj tretí raz musíte robiť to isté (pravidlo Dona Robertsa Do tretice ) 2. pred pridaním funkcionality 3. pri opravách chýb 4. ak sú v programoch zložité štruktúry podmienok (Kent Beck) Problémy refaktoringu: 1. Databáza 2. Zmena publikovaného rozhrania, zavrhované rozhranie, vlastníctvo kódu 3. Ťažko refaktorizovateľné zmeny
Kedy nerefaktorovať ak to nemá zmysel, ak je kód príliš chybový a nevhodný Refaktoring spomaľuje kód, ale umožňuje následné ladenie rýchlosti kódu. Pachy v kóde a ich refactoring podľa katalógu 1 Duplicitný kód 1.1 Duplicitný kód v metódach triedy - Vyňať metódu 1.2 Duplicita v súrodeneckých podtriedach - Vyňať metódu - Presunúť metódu/položku vyššie 1.3 Podobný kód v súrodeneckých podtriedach - Vyňať metódu - Vytvoriť šablónovú metódu (vzor Template method) 1.4 Podobná funkcionalita s iným algoritmom - Nahradiť algoritmus 2 Dlhá metóda - Vyňať metódu, - nahradiť okomentovaný kód metódou s dobrým názvom - nahradiť cyklus alebo podmienky funkciou/metódou Rozložiť podmienku (9.1, s.227) - nahradiť dočasnú premennú (raz použitú) dotazom Vložiť metódu (6.3, s.129) - nahradiť dlhý zoznam parametrov objektom Zaviesť objekt pre parametre (10.9, s.275), Zachovať celý objekt (10.7, s.269) - Nahradiť metódu objektom metódy (6.8, s. 142) ak sa nedá vyňať pre množstvo použitých lokálnych premenných 3 Veľká trieda - Vyňať triedu (7.3, s.153) pomocou Presunúť položku, Presunúť metódu, (vzniká asociácia) - Vyňať podtriedu (11.6, s.305) pomocou Presunutia metódy nižšie, Presunutia položky nižšie, Premenovať metódu, Nahradenie konštruktora továrenskou metódou (Factory method), Nahradiť podmienku polymorfizmom (State, Strategy) (9.6, s.241) ak vznikajú aj rôzne nároky na objekty triedy Vyňať triedu [1]
4 Dlhý zoznam parametrov - Nahradiť parameter explicitnou metódou (10.6, s.266), ak prvý parameter určuje druh položky a druhý hodnotu - Zachovať celý objekt (10.7, s.269) a nepoužívať ako parametre jeho properties (napr. namiesto obj().od, obj().do použiť obj() ako parameter a pod.) - Nahradiť parameter metódou (10.8, s.272), nechať metódu, nech si parametre dosadí sama vyvolaním metódy - Zaviesť objekt pre parametre (10.9, s.275) vytvoriť objekt ako v 10.7 5 Protichodné zmeny Zmena jednej triedy rôznymi spôsobmi (rôzne metódy) pre zmenu rôznych funkcionalít - Vyňať triedu a rozdeliť tak jej funkcionalitu do sam. celkov, metódy pre jeden typ zmeny (zavedenie nového produktu, novej technológie a pod.) ponechať spolu v jednotlivých triedach. 6 Rozptýlené úpravy Zmena na mnohých miestach kvôli zmene jednej funkcionality - Presunúť položku, Presunúť metódu do jednej triedy - Vložiť triedu Vložiť triedu [1] 7 Chýbajúce schopnosti (metódy) - Presunúť metódu - Vyňať metódu a Presunúť metódu ak používa len časť kódu metódy 8 Dátové zhluky - Vyňať triedu najmä pre polia, zmysluplné zväzky položiek (7 Chýbajúce schopnosti) - Zachovať celý objekt (10.7, s.269) - Zaviesť objekt pre parametre (10.9, s.275) 9 Primitívna obsesia (zaťaženie na jednoduché typy - len nie triedy)
- Nahradiť dátovú položku objektom - Nahradiť kód typu triedou (8.13, s.211) ak neovplyvňuje switch, inak: - Nahradiť kód typu podtriedami (8.14, s.216) alebo Nahradiť kód typu stavom alebo stratégiou (8.15, s.219), kde je možné Nahradiť podmienku polymorfizmom (nahradiť podmienené výrazy v riadiacich štruktúrach if-else, switch jediným riadkom pomocou polymorfie - pozri vzor Strategy v súbore OOANS03Vzory s. 43, podobné techniky sú použité aj vo vzore State a Factory method) - pre skupinu položiek Vyňať triedu - pre parametre Zaviesť objekt pre parametre (10.9, s.275) - Nahradiť pole (atribútov: Meno, Priezvisko, Telefón) objektom (8.5, s.184), toto nie je nahradenie kontajnerom (Zapúzdriť kontajner) Nahradiť kód typu podtriedami [1] Nahradiť kód typu stavom alebo stratégiou [1] 10 Príkazy switch - Vyňať metódu a Presunúť metódu, Nahradiť kód typu podtriedami (8.14, s.216) alebo Nahradiť kód typu stavom alebo stratégiou (8.15, s.219), Nahradiť podmienku polymorfizmom (9.6, s.241) - ak switch nie je dôležitý a mohutný, netreba polymorfiu, stačí Nahradiť parameter explicitnou hodnotou (10.6, s.266), Zaviesť objekt null
11 Paralelná hierarchia dedičnosti Špeciálny prípad rozptýlených úprav (vždy je potrebné vytvárať odpovedajúce podtriedy v rôznych hierarchiách) - Presunúť metódu a Presunúť položku 12 Lenivá trieda - Zrušiť hierarchiu pre nečinné triedy - Vložiť triedu pre nevýznamnú triedu 13 Špekulatívna všeobecnosť - Zrušiť hierarchiu pre zbytočné abstraktné triedy - Vložiť triedu pri nepotrebných odkazoch - Odstrániť parameter (nevyužitý) - Premenovať metódu (s podivným abstraktným názvom) 14 Dočasná položka - Vyňať triedu pre málo používané premenné, používané len za určitých okolností (alebo vôbec vďaka refaktoring), vytvoriť opusteným premenným domov a neskôr ho zrušiť, - Vytvoriť objekt null (9.7, 245) pre opakujúci sa kód, ošetrujúci alternatívne stavy null - Vyňať triedu pre atribúty, používané len pre algoritmus, Vyňať metódu a Presunúť metódu a vytvoriť triedu metódy 15 Zreťazené správy Odstrániť sekvencie get metód alebo dočasných premenných na vzdialený objekt - Skryť delegáta na konci, metódu vykonávajúceho požadovanú činnosť a zaviesť prostredníka delegátovi pomocou zapúzdrujúcej metódy - Vyňať metódu a Presunúť metódu a posunúť tak úsek v kóde bližšie v reťazci
Skryť delegáta [1] 16 Prostredník - Odstrániť prostredníka (7.6, 162), ak je zbytočný a vieme volať metódu objektu priamo - Vložiť metódu a posunúť ju tak do volajúcej triedy - Nahradiť delegovanie dedičnosťou (11.12, 327) a z prostredníka tak urobiť podtriedu delegáta, ktorá tak dedením získa priamo túto metódu Nahradiť delegovanie dedičnosťou [1] 17 Nevhodná dôvernosť - Presunúť metódu a Presunúť položku do volajúcej triedy - Vyňať triedu ak majú triedy spoločné záujmy, - alebo oddeliť inou triedou postupom Skryť delegáta - Zmeniť obojsmerné prepojenie na jednosmerné - Nahradiť dedičnosť delegovaním aby sa z nadtriedy, poskytujúcej príliš veľa dôvernosti vznikol delegát pre bývalú podtriedu
Nahradiť dedičnosť delegovaním [1] 18 Alternatívne triedy s rôznymi rozhraniami - Presunúť metódu ak nestačí Premenovať metódu, aby sa vyrovnali protokoly - Vyňať rodičovskú triedu ak by bolo potrebné presúvať mnoho kódu 19 Neúplná knižničná trieda - Zaviesť cudziu metódu (7.7, s.164) ak je len málo nových požadovaných funkcií: vytvorí sa metóda u klienta, inštancia knižničnej triedy sa jej predá ako parameter - Zaviesť miestne rozšírenie (7.8, s.166): vytvoriť podtriedu knižničnej triedy s novými metódami, alebo vytvoriť obaľujúcu triedu (s inštanciou kniž. triedy) 20 Dátová trieda - Zapúzdriť položku (Get(), Set()) - Zapúzdriť kontajner (Get(), Add(), Remove()) - Odstrániť prístupovú metódu pre zápis, ak nechceme meniť obsah položky - Presunúť metódu z inej triedy, ak používa iná trieda položky - Vyňať metódu = vyňať len časť metódy z volajúcej, ak sa nedá celá presunúť - Skryť metódu ak je používaná len interne 21 Odmietnuté dedičstvo - Presunúť metódu a Presunúť položku s nevyužitými vlastnosťami do novej triedy na rovnakej hierarchickej úrovni ako je odmietajúca trieda - Odmietnutie rozhrania je vážnejšie, vtedy je potrebné použiť Nahradiť dedičnosť delegovaním pretože hierarchia sa stáva zbytočnou 22 Komentáre - Vyňať metódu ak je potrebný komentár pre neprehľadný, dlhý kód metódy
- Premenovať metódu ak je potrebný komentár a názov nie je samopopisný - Zaviesť predpoklad (explicitný predpoklad Assertion) (9.8, s.252) 246, 88, 84, 354, Literatúra: [1] Fowler Martin: REFAKTORING, Zlepšenie existujúceho kódu, Addison Wesley -Grada Publishing, Praha, 2003
Ken Pugh: Prefactoring, O Reilly, 2005 Extreme Abstraction (specify operation before you specify the details, what and not how ), Extreme Separation (of concerns), Extreme Readability Splitters can be lumped more easily than lumpers can be split (it is easier to combine two concepts than it is to separate them) Separate Policy From Implementation (keeping the what separated from how makes the what more readable and maintanable) Keep business rules separate from other logic Separating Concerns - Split responsibilities to simplify each method and class Determining Classes - Place methods in classes based on what they need Refactoring, AM, XP, AOP Clear Names and symbolic names Do A Little Job Well (Unix pipes and filters) And You May Be Called Upon Often (Methods and classes that perform specific jobs can be reused more often) Get something working (Create something basic before adding refinements) Test the interfaces, not the implementation Validate, validate, validate Don t be silent The easiest code to debug is that which is not written (Never write functionality that already exists in usable form) Don t reinvent the wheel Prototypes are worth than thousand words (a picture of an interface is, such as screen, can be more powerful than just a description) Think about the big picture (congruent decisions with the big picture) Create interface contracts (preconditions (assertions, validations of input data), postconditions) Explicitness beats implicitness & a few more words give meaning Readability (Fortran vs. Java):CMPORB vs. ORPCMP (compute orbital elements vs. compare orbital elements) Consistency is a form of simplicity Types: most strings are more than just a string: zipcode, city, state, title, prefix (Mr., Mrs.)
Eliminate duplications before it occurs Don t Repeat Yourself (DRY Hunt, Thomas: The pragmatic programmer: From journeyman to master, Addison Wesley, 1999) every piece of knowledge must have a simple representation Dealing with deviations and errors, assertions only during testing? Make the system right, before you make it fast don t speed until you know where you are going There is usually a better solution, but you can stop with good enough. Nothing is perfect. Analysis paralysis, Design paralysis Initial design: CRC (Class Responsibilities - Collaboration) Wirfs-Brock, McKean: Object Design, Addison Wesley, 2002 Global Planning, Local Designing (incremental implementations) Reports can define the system Incremental implementations Testing Functionality: if it can t be tested, don t require it (ther is no way to determine whether you have met it) Different Classes Different Objects: don t overclassify separate on behavior, not on data Use state based analysis to examine object behavior Avoid premature inheritance Inheritance needs time to evolve Avoid premature generalization Solve the specific problem before making the solution general Declarative-style programming can provide flexibility without code changes Solve the specific problem before making the solution general Communicate with Text (comma-delimited files or XML) between programs, not within programs Isaac Asimov (I, robot): three laws for robots (p.71). Three laws for an object: 1. An object shall do what its methods say it does 2. An object shall do not harm 3. An object shall notify it user if it is unable to perform a requested operation (never be silent) Polymorphism different implementations Think interfaces, not inheritance