13
Tags:
diverse
Skrevet af
Bruger #2730
@ 01.07.2008
Bemærk at denne artikel ikke gælder for et enkelt programmeringssprog! I denne artikel vil jeg skrive lidt om hvad refactoring er, hvornår man bruger det samt hvordan det hænger sammen med Antipatterns. Mange steder hører man om "Konstant refactoring" og hvor godt det er for softwaren. Man hører også at refactoring er en af grundstenene i SCRUM og XP metoderne. Men hvad betyder det?
Antipatterns
Før vi går i dybden med refactoring og hvad det betyder for et projekt vil jeg beskrive nogle af de ting der gør at man som udvikler begynder at overveje at udføre refactoring. Antipatterns er i bund og grund en række klassiske fejl man som udvikler kan lave og som det er en god ide at kende og være opmærksom på. Antipatterns er også som navnet antyder det modsatte af et pattern, hvor et pattern er en god og sikker retningslinie man bør vælge hvis man har mulighed for det, så er Antipatterns det nøjagtigt modsatte. Et klassisk Antipattern er "Swiss Army Knife" (den klassiske røde lommekniv vi alle har haft, som har et utal af forskellige knive, oplukkere og skruetrækkere) Antipattern'et, det er en klassisk fejl og et tegn på manglende overblik og design af klassen.
Det typiske problem handler og lav sammenhæng (cohesion) i klassen. En "normal" klasse der er veldesignet har en hel klar definition og et helt klart ansvarsområde med funktionalitet der ligger inden for det ansvarsområde. Det betyder også at der er en høj grad af sammenhæng i klassen og at alle funktionaliteter falder naturligt ind i det ansvarsområde. Skulle man som udvikler så støde ind i en klasse der er dårligt designet og ikke har noget klart defineret ansvarsområde, men som kan en hel masse forskellige (her er sammenhængen med en "Swiss Army Knife") så ved man også hvad det er man har opdaget, og senere ved man også hvordan man retter op på det. Ofte opstår et "Swiss Army Knife" antipattern med tiden og hvor flere forskellige udviklere har arbejdet på det samme uden at se det ansvarsområde den enkelte klasse har haft, eller bare ikke ved bedre. Et eksempel på en klasse der passer på "Swiss Army Knife" antipattern'et er en klasse der eksempelvis hedder "Bruger" og som et naturligt ansvarsområde står for at håndtere alt der er brugerrelateret: Login(), Logout(), Opret(), Slet(), Rediger(). Hvis man i sådan en simpel klasse skulle støde på en metode som står for at beregne moms for indkøbte varer (BeregnMoms()), ville vi nok lige rynke lidt på brynene, da den ikke helt passer ind i det ansvarsområde som denne klasse har. Man kan argumentere for at det ville passe ind i en nedarvning af en bruger der hedder "Indkøber", men ikke direkte på en generisk bruger. Dette er i simpleste form et Antipattern. Disse tegn på dårlig design og Antipatterns er symptomer der skal behandles inden de kommer for langt ud, dette kaldes ofte for "Bad Smells".
Refactoring
Refactoring er behandlingen på de symptomer som der er fundet ved "Bad Smells" og/eller Antipatterns. Rafactoring er en process hvor man omskriver den eksisterende funktionalitet til en mere robust og mere vedligeholdbar løsning. Man tilføjer ikke mere funktionalitet ved en refactoring! Det er kun en strukturel ændring. Hvis vi tager udgangspunkt i det tidligere eksempel med en "Bruger" klasse der har en grim metode der i princippet ikke hører hjemme i denne klasse ville den korrekte løsning i dette tilfælde være en af to løsninger. Man kan flytte metoden til en eksisterende klasse der håndterer indkøb, og hvor denne metode ville passe naturligt ind. Den anden løsning ville være at oprette en ny klasse som fremadrettet skal håndtere indkøb og som en naturlig del skal kunne beregne moms af nogle indkøb. Bemærk at introduktionen af en ny klasse eller flytningen af en metode til en anden klasse ikke er ny funktionalitet, men blot en strukturel ændring i koden.
Implementeringen
Selve implementeringen af en løsning på "swiss army knife" antipatternet er som tidligere skrevet at genskabe den høje cohesion ved at trække metoden/erne der er problemer ud i andre klasser. I det nævnte eksempel kan løsningen komme til at se således ud, bemærk at implementeringen af metoderne er fjernet for overskuelighedens skyld:
public class Bruger
{
public void Opret();
public void Slet();
public void Rediger();
public bool Login();
public void Logout();
}
public class Indkøber() : Bruger
{
public float BeregnMoms();
}
Dette er en ændring i forhold til en tidligere version af klassen bruger hvor BeregnMoms() metoden fandtes på denne klasse. Den er ny refactoret ud i en ny/eksisterende nedarvning hvilket skaber et bedre design. Dette er en simpel refactoring.
Udtræk fra metoder
Af andre lige så trivielle refactoring metoder kan nævnes det at trække sektioner af kode ud i egne metoder, dette gøres ofte for at opnå isolering af forretningslogik. Dette er illustreret af nedenstående kode:
public void PrintFaktura()
{
Bruger bruger = Bruger.Load(5); //loader bruger nummer 5
Console.WriteLine("******************");
Console.WriteLine("** FAKTURA **");
Console.WriteLine("******************");
Console.WriteLine("Navn: "+bruger.Navn);
int beløb = 0;
foreach(int tmp in Bruger.Indkøb)
{
beløb += tmp;
}
Console.WriteLine("Beløb: "+beløb.ToString());
Console.WriteLine("******************");
Console.WriteLine("* Skal betales om*");
Console.WriteLine("* 14 dage. *");
Console.WriteLine("******************");
}
Dette simple eksempel indeholder en del der kan refactores ud i nogle hjælpemetoder. Som vist nedenunder:
public void PrintFaktura()
{
PrintTop();
Bruger bruger = Bruger.Load(5); //loader bruger nummer 5
Console.WriteLine("Navn: "+bruger.Navn);
Console.WriteLine("Beløb: "+BeregnBeløb().ToString());
PrintBund();
}
public void PrintTop()
{
Console.WriteLine("******************");
Console.WriteLine("** FAKTURA **");
Console.WriteLine("******************");
}
public void PrintBund()
{
Console.WriteLine("******************");
Console.WriteLine("* Skal betales om*");
Console.WriteLine("* 14 dage. *");
Console.WriteLine("******************");
}
public float BeregnBeløb()
{
int beløb = 0;
foreach(int tmp in this.Indkøb)
{
beløb += tmp;
}
return beløb;
}
Der er nu blevet introduceret et par nye metoder på Bruger klassen, hjælperutinerne til at skrive til konsollen er ikke så interessante. Derimod er BeregnBeløb(); meget mere interessant, da vi har hevet en del af forretningslogikken ud i en metode, der kan genbruges af alle andre der skal beregne dette beløb. Denne logik skal ikke længere vedligeholdes alle steder hvor det skal beregnes, men alle kan benytte denne metode der nu stilles til rådighed. Samtidig er det nemmere at vedligeholde og overskue.
Opsamling
Refactoring er en strukturel ændring af eksisterende kode. Man refactorer når man har identificeret nogle Antipatterns/"Bad Smells" i sin kode. Hvis man igennem sit projekt har en løbende refactoring er det nemt og overskueligt at lave en refactoring, mens hvis man venter til softwaren er implementeret, løber ind i en masse og langt større problemer med struktur. Derfor er "Konstant refactoring" en god ting, og specielt i metoder der er meget dynamiske og hvor den fremtidige implementering er svær at forudse (typisk de agile udviklingsmetoder som SCRUM og XP) da de som oftest er drevet af kunder. Der er i denne artikel vist eksempler på to simple typer af refactoring og en enkelt "bad smell" som er sammenfaldende med et antipattern. Dette er arbejde man mere eller mindre bevidst hele tiden udfører i sit arbejde, men ved at blive mere opmærksom på eksempelvis bad smells kan man på sigt mindske den tid der bliver brugt på at vedligeholde og videreudvikle sit software (hvilket er omkring 70% af den samlede udviklingstid).
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (6)
Helt klart 5! Way to go. Det er lige præcis den slags artikler udvikleren mangler!
Dejligt at få sat nogle begreber på rod i koden. Jeg synes den fortjener 5, selv om strukturering i koden falder mig lidt naturligt
Noget i retningen af det jeg manglede.
syntes det er fedt at se nogle kode eksempler der viser hvordan man skal strukturere sin kode
Jeg er sikkert negativ...
Hvis du vil skrive om objektorienteret design, så skriv hvordan man udfører det fra starten.
Hvis du vil lave Refactoring, så beskriv hvordan en sådan flytning af en metode foretages automatisk i et værktøj.
Jeg vil gerne skrive om objektorienteret design! Men faktum er at de fleste af de projekter men løber ind i som udvikler er lavet af andre eller man arbejder i fællesskab med andre om et produkt. Oftest er man ikke med fra starten, og starten er typisk flere år siden. Så jo, jeg kunne sagtens skrive om hvordan man udfører objektorienteret design fra starten, men hvorfor? Det er sikkert ikke den situation man havner i alligevel! Det svære er jo at tilpasse sig situationen og vide hvor og hvornår man skal bygge om. Det er heller ikke sikkert at man fra starten af vil bygge et stort forkromet fundament til sin applikation (det bør man heller ikke) men vælger den agile approach til tingene og laver constant refactoring og dermed lidt efter lidt når til de steder, hvor der skal refactores. Og lidt efter lidt får bygget den funktionalitet der skal være. Faktum er at dette hurtigt virker som lapper, men for at undgå dette er det en væsentlig ting at man som udvikler ser dette inden det kommer for vidt og griber ind.
Hvis jeg skal skrive om refactoring ud fra hvordan man gør det i et specielt værktøj, lærer man jo ingenting!!! (det er heller ikke sikkert at alle bruger samme værktøj). Det er, som jeg ser det en parallel til at lave "cut/paste" udvikling, man forstår ikke det man laver, men dykker ukritisk ud i hvordan dit værktøj mener det skal laves. Jeg vil hellere fortælle omkring principperne og det praktiske i det, i stedet for at fortælle dig hvordan du hopper over hvor gærdet er lavest.
Damn, nu kan jeg se hvorfor mine små projekter går i stå, når de bliver store. Jeg har en del liggende, der er druknet i lange funktioner(metoder i java).
Du skal være
logget ind for at skrive en kommentar.