# Refactoring Refactoring er processen hvor du omskriver noget til noget der er bedre. F.eks. hvis du har følgende opskrift på en banankage (taget fra valdemarsro.dk/banankage/) : "*Forvarm din ovn til 175 grader Celsius (med varmluft). I en stor skål piskes 150 g blødt, stuetempereret smør, 175 g sukker og 1 tsk vaniljesukker sammen, indtil blandingen er luftig. Tilsæt 4 æg, ét ad gangen, og pisk godt efter hver tilsætning. Mos 5 meget modne bananer på en tallerken med en gaffel, og fold de moste bananer og 40 g hakkede valnødder i dejen. I en anden skål blandes 175 g hvedemel, 1 tsk bagepulver og en knivspids salt sammen, og denne tørre blanding sigtes i dejen. Vend forsigtigt dejen sammen, indtil den er ensartet. Læg bagepapir i bunden af en springform med en diameter på 20 cm, smør siderne med smør, og hæld dejen i. Bag kagen i den forvarmede ovn i 45-50 minutter, eller indtil en tandstik i midten af kagen kommer ud ren. Lad kagen køle af på en bagerist. Når kagen er afkølet, fordeles 50 g finthakket mælkechokolade over toppen, så den smelter direkte på kagen, og derefter drysses 40 g finthakkede valnødder som pynt.*" Så kan du omskrive den til følgende: "***Ingredienser*** - *150 g smør, blødt og stuetempereret* - *175 g sukker* - *1 tsk vaniljesukker* - *4 æg* - *5 bananer, meget modne* - *40 g valnødder, hakkede* - *175 g hvedemel* - *1 tsk bagepulver* - *1 knivspids salt* ***Til pynt*** - *50 g mælkechokolade, finthakket* - *40 g valnødder, finthakkede* ***Fremgangsmåde** Pisk smør, sukker og vaniljesukker sammen, til det er luftigt. Pisk derefter et æg i ad gangen.* *Mos bananerne på en tallerken med en gaffel og rør banan og valnødder i dejen.* *Bland hvedemel, bagepulver og salt sammen og sigt det i dejen. Vend dejen sammen, til den er ensartet.* *Kom bagepapir i bunden af en springform på 20 cm i diameter, smør siderne med smør og kom dejen i. Bag kagen i en forvarmet ovn ved 175 grader varmluft i 45-50 minutter.* *Lad kagen køle af på en rist. Fordel chokoladen på den varme kage og lad den smelte direkte på kagen. Drys med valnødder.*" Her har du refactoret opskriften, altså omskrevet den til en mere forståelig og overskuelig opskrift end før. Det samme er gældende for kode - det at omskrive et stykke kode til noget der er bedre. Spørgsmålene bliver så bare: 1. Hvornår ved man at noget kode trænger til at blive refactoret? 2. Hvordan skal koden refactores? For at svare på det skal vi kigge på '**kode lugte** (*code smells*)' # Kode lugte (code smells) Kode lugte er genkendelige 'lugte' som indikerer, at ens kode trænger til at blive refactoret. Det svarer lidt til, at man står og hakker løg og samtidig er ved at stege nogle gulerødder, og så lige pludselig lugter det brændt - her har man en lugt, som er genkendelig og som tyder på, at noget ikke er som det skal være. Herefter lærer man hurtigt at skrue ned, tage det af varmen, eller smide hele lortet ud og starte forfra med madlavning, hvis det begynder at lugte brændt. Det er altså typiske løsninger man foretager sig alt efter 'lugten' (kan jo også være visuelt - ser muggen ud; eller smag - smager surt). Men, der kan være flere løsninger til en lugt, hvor det er lidt op til den, der laver mad, der bedst vurderer, hvordan problemet skal håndteres i situationen. Det samme er gældende for programmering. Når man står på en *kode lugt*, så er der ofte mere end én måde at fjerne lugten på. I starten er en ny programmør ikke så god til at identificere lugtene. Og selv hvis de identificerer dem, er de nødvendigvis heller ikke bevidst om, hvordan lugten kan fjernes. Heldigivs findes der en oversigt over almindelige **kode lugte** og almindelige **refactoring teknikker**, som kan bruges til at fjerne lugten. I programmering har man overordnet set 5 grupper af **kode lugte**: * **Oppustede** (*bloaters*) * **Objektorienterede misbrugere** (*object-orientation abusers*) * **Forandringsforhindrerne** (*change preventers*) * **Undgåelige** (*dispensables*) * **Koblingerne** (*couplers*) Man har også forskellige **refactoring teknikker**, som er opdelt i 6 grupper: * **Sammensætte metoder** (*composing methods*) * **Flytte funktioner mellem objekter** (*moving features between objects*) * **Organisere data** (*organizing data*) * **Forenkling af betingede udtryk** (*simplifying conditional expressions*) * **Forenkling af metodekald** (*simplifying method calls*) * **Håndtere generalisering** (*dealing with generalization*) Som sagt så kan en **kode lugt** fjernes ved flere forskellige **refactoring teknikker**. Hvis du har et beskidt køkkenbord, så kan du bruge en våd klud med sæbe, en vådserviet, eller du kan bruge køkkenspray og køkkenrulle - 3 forskellige '**refactoring teknikker**' til at løse en '**kode lugt**'. Samtidig så kan en **refactoring teknik** bruges til at fjerne adskillige **kode lugte**. Ligesom man godt kan bruge køkkenspray til at gøre andet rent udover køkkenet. ## Oppustede (bloaters) Hvis vi ser på vores opskrift eksempel med banankagen: "*Forvarm din ovn til 175 grader Celsius (med varmluft). I en stor skål piskes 150 g blødt, stuetempereret smør, 175 g sukker og 1 tsk vaniljesukker sammen, indtil blandingen er luftig. Tilsæt 4 æg, ét ad gangen, og pisk godt efter hver tilsætning. Mos 5 meget modne bananer på en tallerken med en gaffel, og fold de moste bananer og 40 g hakkede valnødder i dejen. I en anden skål blandes 175 g hvedemel, 1 tsk bagepulver og en knivspids salt sammen, og denne tørre blanding sigtes i dejen. Vend forsigtigt dejen sammen, indtil den er ensartet. Læg bagepapir i bunden af en springform med en diameter på 20 cm, smør siderne med smør, og hæld dejen i. Bag kagen i den forvarmede ovn i 45-50 minutter, eller indtil en tandstik i midten af kagen kommer ud ren. Lad kagen køle af på en bagerist. Når kagen er afkølet, fordeles 50 g finthakket mælkechokolade over toppen, så den smelter direkte på kagen, og derefter drysses 40 g finthakkede valnødder som pynt.*" Kan vi se at det her det her paragraf indeholder alt fra hvordan du: * forbereder ingredienserne, * bearbejder ingredienserne, * bager kagen, og * hvordan du pynter den Man kan sige at den her opskrift er **oppustet** (*bloated*) i det at du har et enkelt paragraf som er blevet så stort så det utroligt svært at arbejde med. F.eks. er det ikke så nemt at hvis du lige skal tjekke om det f.eks. var en halv eller hel tsk bagepulver. Hvis vi så refactor det ligesom i starten så får vi: "***Ingredienser*** - *150 g smør, blødt og stuetempereret* - *175 g sukker* - *1 tsk vaniljesukker* - *4 æg* - *5 bananer, meget modne* - *40 g valnødder, hakkede* - *175 g hvedemel* - *1 tsk bagepulver* - *1 knivspids salt* ***Til pynt*** - *50 g mælkechokolade, finthakket* - *40 g valnødder, finthakkede* ***Fremgangsmåde** Pisk smør, sukker og vaniljesukker sammen, til det er luftigt. Pisk derefter et æg i ad gangen.* *Mos bananerne på en tallerken med en gaffel og rør banan og valnødder i dejen.* *Bland hvedemel, bagepulver og salt sammen og sigt det i dejen. Vend dejen sammen, til den er ensartet.* *Kom bagepapir i bunden af en springform på 20 cm i diameter, smør siderne med smør og kom dejen i. Bag kagen i en forvarmet ovn ved 175 grader varmluft i 45-50 minutter.* *Lad kagen køle af på en rist. Fordel chokoladen på den varme kage og lad den smelte direkte på kagen. Drys med valnødder.*" Her kan vi se at vi har: 1. Opdelt opskriften så vi har 2 overordnet som har med henholdsvis ingrediernes at gøre og fremgangsmåden. Sektionen til ingredienserne er splittet op i 2 - hvor den ene af dem er til de ingredienser som skal blandes som og bages til kagen, og den anden er de ingredienser vi bruger til at pynte den færdigbagte kage med. 2. Ingredienserne er struktureret i en liste format så vi nemt kan tjekke om vi har dem. 3. Fremgangsmåden er blevet opdelt så vi har mindre bider af tekst som hver beskriver en opgave af det at bage kagen. Altså vores opskrift gik fra at være 1 stor hulter-til-bulter **oppustet** paragraf hvor alt var blandet sammen til at blive langt mere overskueligt. I programmering findes der også **oppustet** kode vi kalder dem for **bloaters**. De typiske **bloaters** der findes er: * **Lang metod** (*long method*) * **Stor klasse** (*large class*) * **Primitiv besættelse** (*primitive obsession*) * **Lang parameter liste** (*long parameter list*) * **Data klumper** (*data clumps*) **Kort fortalt** **Lang metode** (*long method*) Forestil dig at du skal lave en ret som består af 5 elementer. I stedet for at samle alle elementerle i én metode, er det bedre at have dem adskilt i hver deres metode. Dette er en bloater, fordi din metode er for stor, er uoverskuelig og har et for stort ansvar. **Stor klasse** (*large class*) Hvis du skal lave en 3 retters menu så er det bedre at tænke på hver ret for sig selv end at tænke på det som en stor ret. På samme måde er det bedre at opdele en stor klasse, der håndterer en 3-retters menu, i tre 3 separate klasser, hvor hver har med én ret. Dette er en bloater, fordi din klasse er for stor, er uoverskuelig og har et for stort ansvar. **Primitiv besættelse** (*primitive obsession*) Forestil dig, at du er en bager, der bruger én type mel til alle dine opskrifter. I stedet for vil det være bedre at bruge forskellige typer mel og bruge det, der passer bedst til opskriften. Rugmel til rugbrødet, hvedemel til rundstykker, og så videre. I programmering er det tilvsarende bedre at bruge en 'Person'-klasse til at repræsentere en person i stedet for en 'String'. Dette er en bloater, fordi du ikke tager gavn af de muligheder, som datatyper giver dig og du lader en datatype have et for stort ansvar. **Lang parameter liste** (*long parameter list*) Handler om at hvis din metode tager alt for mange parametre bliver det uoverskueligt og svært at forstå. Ligesom at en opskrift bliver mere uoverskuelig og at sandsynligheden, jo flere ingredienser der skal til, samtidig med at sandsynligheden for at man lige glemmer at købe en ingrediens stiger - det er nemmere at håndtere & huske 5 ingredienser end 50. Dette er en bloater, fordi jo flere parametre en metode tager, jo mere kompleks og svær at vedlighede bliver metoden. **Data klumper** (*data clumps*) Forestil dig at du skal lave en 3 retters menu, og du har en liste ingredienser, men ingredienserne er ikke grupperet efter hver ret. Det kan hurtigt blive svært at finde ud af, hvilke ingredienser, der passer til hvilken ret. På samme måde kan man have klumper af data i kode, hvor data, der passer sammen, ikke er grupperet. Dette er en bloater, fordi det gør koden uorganiseret og svær at forstå. Når vi så spotter disse **bloater** *code smells* så er det så vi skal håndtere dem - refactor dem. En code smell f.eks. **lang metode** kan godt have flere løsninger. Lidt ligesom vi kunne have struktureret opskriften på en anden måde hvis vi havde lyst til det. Pointen er bare at vi prøver at gøre det vi refactor bedre. ### Lang metode (long method) Forestil dig, at du har fået en opskrift til at lave en kompleks ret, som består af adskillige elementer. I stedet for at have en separat sektion til hvordan du forbereder ingredienserne til hvert element, tilbereder hvert element, hvordan du sammensætter dem til hele retten, så er opskriften bare en lang liste af trin, der skal udføres. Dette gør, at det bliver svært at se den overordnede struktur af opskriften. F.eks.: Lad os sige du skal have følgende elementer til din ret: * Shiitake marmelade * Kartoffel puree * Sous vide okse bøf * Ristet hvidløgs chips Hvor hvert element har sin egen opskrift, og du har så en overordnet opskrift, der sammensætter det. Så har du bare fået en stor opskrift, hvor alle trin for alle elementer er tilstede. Du kunne f.eks. have følgende metode i Java for at lave din ret: ```Java! public void kompleksRet() { // Shiitake marmelade System.out.println("fin hak 70g skalotteløg"); System.out.println("brunoise 26g shiitake svampe"); System.out.println("brunoise 10g hvidløg"); System.out.println("Sauté skalotte løg, svampe, hvidløg i 10g klaret smør & 10g afsmeltet oksemarv"); // osv // Kartoffel puree System.out.println("skræl kartofler"); System.out.println("kog kartofler"); // osv // Sous vide okse bøf System.out.println("Vakuumpak oksebøf"); System.out.println("Tilbered oksebøf i sous vide ved 55 grader celsius"); // osv // Ristet hvidløgs chips System.out.println("Pil hvidløg"); System.out.println("Hak hvidløg"); // osv } ``` Hvis man i stedet puttede det, der passede til hvert enekelt element, i sin egen metode, kunne man få det her: ```Java! public void kompleksRet() { shiitakeMarmelade(); kartoffelPuree(); sousVideOkseBoef(); ristetHvidloegsChips(); } private void shiitakeMarmelade() { System.out.println("fin hak 70g skalotteløg"); System.out.println("brunoise 26g shiitake svampe"); System.out.println("brunoise 10g hvidløg"); System.out.println("Sauté skalotte løg, svampe, hvidløg i 10g klaret smør & 10g afsmeltet oksemarv"); // osv } private void kartoffelPuree() { System.out.println("skræl kartofler"); System.out.println("kog kartofler"); // osv } private void sousVideOkseBoef() { System.out.println("Vakuumpak oksebøf"); System.out.println("Tilbered oksebøf i sous vide ved 55 grader celsius"); // osv } private void ristetHvidloegsChips() { System.out.println("Pil hvidløg"); System.out.println("Hak hvidløg"); // osv } ``` Nu har vi **refactoret** kompleksRet() metoden. Vi kan se, at metoden er blevet langt mere overskuelig, og kan nemt se, hvilke elementer retten består af. Vores originale kompleksRet() metode er eksempel på den **bloater** *code smell*, der kaldes for **lang metode**. Den løsning, vi brugte, kaldes for **udtræk metode** (*extract method*). Vi har altså udtrukket noget kode for vores metode kompleskRet() og flyttet det over i en ny metode og erstattet koden i kompleksRet() til et kald til vores nye metode. F.eks. er ```Java! System.out.println("fin hak 70g skalotteløg"); System.out.println("brunoise 26g shiitake svampe"); System.out.println("brunoise 10g hvidløg"); System.out.println("Sauté skalotte løg, svampe, hvidløg i 10g klaret smør & 10g afsmeltet oksemarv"); ``` i kompleksRet() blevet til: ```Java! shiitakeMarmelade(); ``` Hvor shiitakeMarmelade() er: ```Java! private void shiitakeMarmelade() { System.out.println("fin hak 70g skalotteløg"); System.out.println("brunoise 26g shiitake svampe"); System.out.println("brunoise 10g hvidløg"); System.out.println("Sauté skalotte løg, svampe, hvidløg i 10g klaret smør & 10g afsmeltet oksemarv"); // osv } ``` Vi har altså udtrukket den stump kode fra kompleksRet() til shiitakeMarmelade() **Lang metode** er altså, når vi har en metode, som indeholder for mange linjer kode. Måden vi kunne se, at den var for lang på udover selve længden, er også, at der blev tilføjet kommentare for at beskrive de individuelle elementer til retten. Hvis vi fjernede de kommentarer og havde: ```Java! public void kompleksRet() { System.out.println("fin hak 70g skalotteløg"); System.out.println("brunoise 26g shiitake svampe"); System.out.println("brunoise 10g hvidløg"); System.out.println("Sauté skalotte løg, svampe, hvidløg i 10g klaret smør & 10g afsmeltet oksemarv"); // osv System.out.println("skræl kartofler"); System.out.println("kog kartofler"); // osv System.out.println("Vakuumpak oksebøf"); System.out.println("Tilbered oksebøf i sous vide ved 55 grader celsius"); // osv System.out.println("Pil hvidløg"); System.out.println("Hak hvidløg"); // osv } ``` Så vil det blive endnu mere uoverskueligt. Så ofte hvis man læser en metode og tænker at en kommentar til at 'opdele' det der sker i metoden så er den en god indikator på at vi skal refactor. Udover at det gør koden mere overskuelig og lettere at vedligeholde, er der også andre fordele ved at opdele en lang metode i mindre metoder. 1. Det bliver lettere at genbruge dele af koden i andre dele af programmet, da hver metode har et klart defineret ansvar. 2. Det bliver lettere at finde og rette fejl i koden, da det er nemmere at isolere fejlen, når koden er opdelt i mindre dele.