En snabb introduktion till refaktorisering för nybörjare
Marta Swiatkowska
Junior Software Engineer
Kanske skriver jag om något som är självklart för många, men kanske inte för alla. Refaktorisering är, tycker jag, ett komplicerat ämne eftersom det handlar om att förändra koden utan att påverka dess funktion.
Därför är det obegripligt för vissa att Refaktorisering är faktiskt ett område inom programmering, och det är också en mycket viktig del av programmerarens arbete. Koden är i ständig utveckling, den kommer att modifieras så länge det finns möjlighet att lägga till nya funktioner. Den kan dock anta en form som inte längre gör det möjligt att effektivt lägga till nya funktioner och det skulle då vara enklare att skriva om hela programmet.
Vad är refaktorisering?
Vanligtvis får man svaret att det handlar om att ändra kodens struktur genom att tillämpa en serie refaktoriseringstransformationer utan att påverka kodens observerbara beteende. Detta är sant. Nyligen kom jag också över en definition av Martin Fowler i sin bok "Förbättra utformningen av befintlig kod" där han beskriver Refaktorisering som "att göra stora förändringar i små steg". Han beskriver Refaktorisering som en kodändring som inte påverkar dess funktion, men han betonar att det måste göras i små steg.
Boken förespråkar också att Refaktorisering inte påverkar kodens funktion och påpekar att det inte har någon effekt på att klara testerna vid någon tidpunkt. Den beskriver steg för steg hur man på ett säkert sätt utför Refaktorisering. Jag gillade hans bok eftersom den beskriver enkla knep som kan användas i det dagliga arbetet.
Varför behöver vi refaktorisering?
Oftast kan du behöva det när du vill införa en ny funktionalitet och koden i sin nuvarande version inte tillåter det eller det skulle vara svårare utan ändringar i koden. Det är också användbart i fall där det är olönsamt tidsmässigt att lägga till fler funktioner, det vill säga att det skulle vara snabbare att skriva om koden från grunden. Jag tror att det ibland glöms bort att Refaktorisering kan göra koden renare och mer läsbar. Martin skriver i sin bok hur han utför refaktorisering när han känner obehagliga lukter i koden och, som han uttrycker det, "det lämnar alltid utrymme för det bättre". Och han överraskade mig här genom att se refaktorisering som en del av det dagliga kodarbetet. För mig är koderna ofta obegripliga, att läsa den är lite av en upplevelse eftersom koden ofta är ointuitiv.
Det som utmärker ett väl utformat program är dess ModularitetTack vare detta räcker det med att känna till en liten del av koden för att göra de flesta ändringar. Modularitet gör det också lättare för nya personer att komma in och börja arbeta mer effektivt. För att uppnå denna modularitet måste relaterade programelement grupperas tillsammans, och kopplingarna måste vara begripliga och lätta att hitta. Det finns ingen enskild tumregel för hur detta kan göras. Allt eftersom du vet och förstår mer och mer av hur koden är tänkt att fungera bättre kan du gruppera elementen, men ibland måste du också testa och kontrollera.
En av reglerna för refaktorisering i YAGNIär en akronym för "You Aren't Gonna Need It" och härstammar från eXtreme-programmering (XP) används främst i AgilUtveckling av programvara lag. För att göra en lång historia kort, YAGNI säger att endast aktuella saker ska göras. Det betyder i princip att även om något kan behövas i framtiden, så ska det inte göras just nu. Men vi kan inte heller krossa ytterligare tillägg och det är här modularitet blir viktigt.
När man talar om Refaktoriseringmåste ett av de viktigaste inslagen nämnas, nämligen testerna. I Refaktoriseringmåste vi veta att koden fortfarande fungerar, eftersom Refaktorisering ändrar inte hur den fungerar, utan dess struktur, så alla tester måste godkännas. Det är bäst att köra tester för den del av koden som vi arbetar med efter varje liten förändring. Det ger oss en bekräftelse på att allt fungerar som det ska och det förkortar tiden för hela operationen. Det är detta som Martin pratar om i sin bok - kör tester så ofta som möjligt för att inte behöva ta ett steg tillbaka och slösa tid på att leta efter en transformation som förstörde något.
Refaktorisering av kod utan testning är jobbigt och det finns en stor risk att något går fel. Om det är möjligt är det bäst att lägga till åtminstone några grundläggande tester som ger oss en liten försäkran om att koden fungerar.
De omvandlingar som anges nedan är endast exempel men de är till stor hjälp vid daglig programmering:
Funktionsextraktion och variabelextraktion - om funktionen är för lång, kontrollera om det finns några mindre funktioner som kan extraheras. Detsamma gäller för långa rader. Dessa omvandlingar kan hjälpa till att hitta dubbletter i koden. Tack vare Small Functions blir koden tydligare och mer begriplig,
Byte av namn på funktioner och variabler - att använda rätt namngivningskonvention är viktigt för god programmering. Väl valda variabelnamn kan berätta mycket om koden,
Gruppering av funktionerna i en klass - denna ändring är till hjälp när två klasser utför liknande operationer eftersom det kan förkorta klassens längd,
Åsidosätta den nästlade satsen - om villkoret stämmer för ett specialfall, utfärda en retursats när villkoret inträffar. Dessa typer av tester kallas ofta för guard clause. Om du ersätter en kapslad villkorlig sats med en exit-sats ändras betoningen i koden. If-else-konstruktionen tilldelar båda varianterna samma vikt. För den som läser koden är det ett budskap om att var och en av dem är lika sannolik och viktig,
Införa ett specialfall - om du använder vissa villkor i din kod många gånger kan det vara värt att skapa en separat struktur för dem. Som ett resultat kan de flesta specialfallskontroller ersättas med enkla funktionsanrop. Ofta är det vanliga värdet som kräver särskild bearbetning null. Därför kallas detta mönster ofta för nollobjektet. Detta tillvägagångssätt kan dock användas i alla specialfall,
Ersättning av den villkorliga instruktionspolymorfismen.
Exempel
Detta är en artikel om Refaktorisering och ett exempel behövs. Jag vill visa ett enkelt refaktoriseringsexempel nedan med användning av Åsidosätta det nästlade uttalandet och Ersättning av den villkorliga instruktionspolymorfismen. Låt oss säga att vi har en programfunktion som returnerar en hash med information om hur man vattnar växter i verkligheten. Sådan information skulle förmodligen finnas i modellen, men i det här exemplet har vi den i funktionen.
def vattning_info(anläggning)
resultat = {}
om anläggningen.är_en? Sukulent || växt.är_en? Kaktus
resultat = { vatten_mängd: "Lite grann" , how_to: "Från botten", vattning_duration: "2 veckor" }
elsif plant.is_a? Alocasia || plant.is_a? Maranta
resultat = { vattenmängd: "Stor mängd", hur_to: "Som du vill", vattning_duration: "5 dagar" }
elsif plant.is_a? Peperomia
result = { water_amount: "Dicent mängd",
hur_till: "Från botten! de gillar inte vatten på bladen",
vattning_duration: "1 vecka" }
annat
resultat = { vattenmängd: "Dicent mängd",
how_to: "Som du vill",
vattning_duration: "1 vecka"
}
slut
returnera resultat
slut
Och så vidare med allt, tills vi kommer till en funktion som ser ut så här:
def vattning_info(anläggning)
returnera resultat = { vattenmängd: "Lite grann" , howto: "Från botten", vattningduration: "2 veckor" } if plant.isa? Sukulent || plant.is_a? Kaktus
return resultat = { vattenmängd: "Stor mängd", howto: "Som du vill", vattningens varaktighet: "5 dagar" } if plant.isa? Alocasia || plant.is_a? Maranta
return resultat = { vattenmängd: "Dicent mängd",
hur man gör: "Från botten! de gillar inte vatten på bladen",
vattnets varaktighet: "1 vecka" } if plant.is_a? Peperomia
return resultat = { vattenmängd: "Dicent mängd",
hur_to: "Som du föredrar",
vattning_duration: "1 vecka"
}
slut
I slutet hade vi redan ett returresultat. Och en god vana är att göra detta steg för steg och testa varje förändring. Du kan ersätta det här if-blocket med ett switch-fall och det skulle se bättre ut direkt, och du skulle inte behöva kontrollera alla if:ar varje gång. Det skulle se ut så här:
def vattning_info(anläggning)
swich växt.klass.till_sträng
fall Suckulent, Kaktus
{ vattenmängd: "Lite grann" , howto: "Från botten", vattning_duration: "2 veckor" }
fall Alocasia, Maranta
{ vattenmängd: "Stor mängd", howto: "Som du vill", vattning_duration: "5 dagar" }
fall Peperomia
{ vattenmängd: "Dicent mängd",
hur_till: "Från botten! de gillar inte vatten på bladen",
vattning_duration: "1 vecka" }
annat
{ vatten_belopp: "Dicent mängd",
hur_to: "Som du föredrar",
vattning_duration: "1 vecka" }
Slut
slut
Och sedan kan du använda Ersätta den villkorliga instruktionen Polymorfism. Detta för att skapa en klass med en funktion som returnerar rätt värde och byter ut dem på rätt plats.
klass Suckulenta
...
def vattning_info()
return { vattenmängd: "Lite grann" , howto: "Från botten" , watering_duration: "2 veckor" }
slut
slut
klass Cactus
...
def vattning_info()
return { vattenmängd: "Lite grann" , howto: "Från botten" , watering_duration: "2 veckor" }
slut
slut
klass Alocasia
...
def vattning_info
return { vattenmängd: "Stor mängd", howto: "Som du föredrar", vattning_duration: "5 dagar" }
slut
slut
klass Maranta
...
def vattning_info
return { vattenmängd: "Stor mängd", howto: "Som du föredrar", vattning_duration: "5 dagar" }
slut
slut
klass Peperomia
...
def vattning_info
return { vatten_belopp: "Dicent belopp",
hur_till: "Från botten! De gillar inte vatten på bladen",
vattning_duration: "1 vecka" }
slut
slut
klass Växt
...
def vattning_info
return { vatten_belopp: "Dicent belopp",
hur_till: "Som du föredrar",
vattning_duration: "1 vecka" }
Slut
slut
Och i huvudfunktionen watering_infofunction kommer koden att se ut så här:
def vattning_info(anläggning)
växt.karta(&:vattning_info)
slut
Naturligtvis kan denna funktion tas bort och ersättas med dess innehåll. Med det här exemplet ville jag presentera den allmänna mönster för refaktorisering.
Sammanfattning
Refaktorisering är ett stort ämne. Jag hoppas att den här artikeln var ett incitament att läsa mer. Dessa refaktoriseringskompetens hjälpa dig att fånga dina buggar och förbättra din rena kodverkstad. Jag rekommenderar att du läser Martins bok (Improving the Design of Existing Code), som är en ganska grundläggande och användbar uppsättning regler för Refaktorisering. Författaren visar olika omvandlingar steg för steg med en fullständig förklaring och motivation och tips om hur man undviker misstag i Refaktorisering. På grund av sin mångsidighet är det en förtjusande bok för frontend- och backend-utvecklare.