Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code
date post
11-Feb-2017Category
Education
view
161download
0
Embed Size (px)
Transcript of Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code
Refactoring C#Legacy Code
Stefan Lieser @StefanLieser
http://refactoring-legacy-code.net
http://refactoring-legacy-code.nethttp://refactoring-legacy-code.net
Houston, weve had a problem.
http://er.jsc.nasa.gov/seh/13index.jpg
Wandelbarkeit
https://pixabay.com/de/chamleon-hautnah-exotische-grn-1414084/
Investitionsschutz = Wandelbarkeit
Nicht-funktionale Anforderungen
Funktionale Anforderungen
Das Problem besteht aus drei Teilen:
Lesbarkeit/Verstndlichkeit ist nicht gegeben Automatisierte Tests fehlen Komplexe Refactorings sind notwendig
Das Dilemma:
Um Tests zu ergnzen, => muss refactored werden.
Um zu refactoren, => mssen Tests ergnzt werden.
KISS - Keep It Simple Stupid Einfach in Bezug auf
Schreiben / Erstellen? Lesen / Modifizieren !
Ein Sicherheitsnetz spannen: Versionskontrolle Continuous Integration Code Reviews Automatisierte Tests
Begrndung fr Refactorings:
NICHT: Clean Code Developer, Prinzipien,Werte, Craftsmen,
Sondern Kundennutzen: Neues Feature Bugfix
Es gibt zwei Artenvon Refactorings:
einfache und
komplexe.
Einfache RefactoringsRename Extract Method Introduce VariableIntroduce Parameter etc.
Vollstndig werkzeuggesttzt durchfhrbar
Komplexe RefactoringsNicht mehr ausschlielich
werkzeuggesttzt durchfhrbar
Einfache RefactoringsLesbarkeit herstellen.
Erkenntnisse ber den Code im Code sichern.
publicdoubleEndpreis(intanzahl,doublenetto){ varnettoPreis=anzahl*netto;returnnettoPreis*1.19;}
publicdoubleEndpreis(intanzahl,doublenetto){ return(anzahl*netto)*1.19;}
publicdoubleEndpreis(intanzahl,doublenetto){ varnettoPreis=anzahl*netto;varbruttoPreis=nettoPreis*1.19;returnbruttoPreis;}
Introduce Variable
Introduce Variable
publicdoubleEndpreis(intanzahl,doublenetto){constdoublemwSt=0.19;varnettoPreis=anzahl*netto;varsteuer=nettoPreis*mwSt;varbruttoPreis=nettoPreis+steuer;returnbruttoPreis;}
publicdoubleEndpreis(intanzahl,doublenetto){ varnettoPreis=anzahl*netto;varbruttoPreis=nettoPreis*1.19;returnbruttoPreis;} Nicht mehr nur
toolgesttzt!
publicIDictionaryToDictionary(stringconfiguration){varsettings=SplitIntoSettings(configuration);varpairs=SplitIntoKeyValuePairs(settings);varresult=InsertIntoDictionary(pairs);
returnresult;}
publicIDictionaryToDictionary(stringconfiguration){returnInsertIntoDictionary(SplitIntoKeyValuePairs(SplitIntoSettings(configuration)));}
Introduce Variable
publicIDictionaryToDictionary(stringconfiguration){//Splitconfigurationintosettingsvarsettings=configuration.Split(';');
//Splitsettingsintokey/valuepairsvarpairs=newList();foreach(varsettinginsettings){varkeyAndValue=setting.Split('=');pairs.Add(newKeyValuePair(keyAndValue[0],keyAndValue[1]));}
//Insertpairsintodictionaryvarresult=newDictionary();foreach(varpairinpairs){result.Add(pair.Key,pair.Value);}
returnresult;}
Extract Method
publicIDictionaryToDictionary(stringconfiguration){varsettings=SplitIntoSettings(configuration);varpairs=SplitIntoPairs(settings);varresult=InsertIntoDictionary(pairs);returnresult;}
foreach(varsettinginsettings){varkey_and_value=setting.Split('=');result.Add(key_and_value[0],key_and_value[1]);}
Extract Method
foreach(varsettinginsettings){AddSettingToResult(setting,result);}
privatestaticvoidAddSettingToResult(stringsetting,Dictionaryresult){varkey_and_value=setting.Split('=');result.Add(key_and_value[0],key_and_value[1]);}
if(DateTime.Now>=weckZeit){using(varsoundPlayer=newSoundPlayer(@"chimes.wav")){soundPlayer.Play();}timer.Stopp();}
Extract Method
if(WeckzeitErreicht()){using(varsoundPlayer=newSoundPlayer(@"chimes.wav")){soundPlayer.Play();}timer.Stopp();}
privateboolWeckzeitErreicht(){returnDateTime.Now>=weckZeit;}
if(WeckzeitErreicht()){using(varsoundPlayer=newSoundPlayer(@"chimes.wav")){soundPlayer.Play();}timer.Stopp();}
privateboolWeckzeitErreicht(){returnDateTime.Now>=weckZeit;}
Extract Method
if(WeckzeitErreicht()){Wecken();}
publicstringFormat(stringvorname,stringnachname,stringstrasse,stringhn,stringplz,stringort){return$"{vorname}{nachname},{strasse}{hn},{plz}{ort}";}
publicstringFormat(Adresseadresse,stringvorname,stringnachname){return$"{vorname}{nachname},{adresse.Strasse}{adresse.Hn},{adresse.Plz}{adresse.Ort}";}
Extract Class from Parameters
publicclassAdresse{privatestringstrasse;privatestringhn;privatestringplz;privatestringort;
publicAdresse(stringstrasse,stringhn,stringplz,stringort){this.strasse=strasse;this.hn=hn;this.plz=plz;this.ort=ort;}
publicstringStrasse{get{returnstrasse;}}...}
publicstring[]CsvTabellieren(string[]csvZeilen){_csvZeilen=csvZeilen;
RecordsErstellen();int[]breiten=SpaltenbreitenErmitteln();TabelleErstellen(breiten);
return_tabelle;}
publicstring[]CsvTabellieren(string[]csvZeilen){varrecords=RecordsErstellen(csvZeilen);int[]breiten=SpaltenbreitenErmitteln(records);vartabelle=TabelleErstellen(breiten,records);
returntabelle;}
Automatisierte Tests ergnzen
Herausforderungen beim Ergnzen von Tests: Abhngigkeiten Aspekte nicht getrennt Ressourcenzugriffe Sichtbarkeit Globaler Zustand
Lsungen: Zunchst vermehrt Integrationstests,
dann Refactoring und Unit Tests ergnzen. Mock Frameworks wie
TypeMock Isolator oder Telerik JustMock einsetzen.
Wenige Integrationstests
Viele Unittests
Viele Integrationstests
Wenige Unittests
usingSystem.Collections.Generic;usingSystem.IO;
namespaceressourcen{publicclassCsvReader{publicIEnumerableRead(stringfilename){varlines=File.ReadAllLines(filename);foreach(varlineinlines){varfields=line.Split(';');yieldreturnfields;}}}}
Integrierter Ressourcenzugriff
[Test,Isolated]publicvoidIsolated_logic_test(){varsut=newCsvReader();Isolate.WhenCalled(()=>File.ReadAllLines("")).WillReturn(new[]{"a;b;c","1;2;3"});
varrecords=sut.Read("egal.csv").ToArray();
Assert.That(records[0],Is.EqualTo(new[]{"a","b","c"}));Assert.That(records[1],Is.EqualTo(new[]{"1","2","3"}));}
Verhalten der Attrappe beschreiben
publicclassPrivatesZeugs{privatevoidSaveToDatabase(stringdata){Console.WriteLine("Ressourcenzugriff..."+data);thrownewException();}
publicvoidDoSomething(){Console.WriteLine("DoSomething...");SaveToDatabase("datendatendaten...");}}
[TestFixture]publicclassPrivateMethoden{[Test]publicvoidPrivate_Methode_nicht_ausfhren(){varsut=newPrivatesZeugs();Isolate.NonPublic.WhenCalled(sut,"SaveToDatabase").IgnoreCall();
sut.DoSomething();}}
Verhalten der Attrappe beschreiben
Komplexe RefactoringsMikado Methode
Change
Change
Change
Change
Change
Change
Change
Change
Change
Hm ist das wirklich clever?
Change
Change
Cha
Change
Cha
Change
B
Cha
Revert!
Change
B
Cha
Change
B
Cha
Change
B
D
Cha
C
Revert!
Change
B
D
Cha
C
Change
B
D
Cha
C
Change
B
D
Cha
C
Change
B
D
Cha
C
E
Revert!
Change
B
D
Cha
C
E
Change
B
D
Cha
C
E
Change
B
D
Cha
C
E
Change
B
D
F
Cha
C
E
Revert!
Change
B
D
F
Cha
C
E
Change
B
D
F
Cha
C
E
Change
B
D
F
Cha
C
E
Change
B
D
F
A
Cha
C
E
Revert!
Change
B
D
F
A
Cha
C
E
Change
Change
Change
Change
Change
Change
Change
Change
Change
Change
Change
Change
Change
Change
Hurra!
B
D
F
A
Change
C
E
B
D
F
A
Change
C
E
Ana l
y s e
Re f a c t o r i ng
NachLsungensuchen
DasMikadoZielnotieren
DasZielbzw.dieaktuelleVorbedingungnaivimplementieren
GibteseinProblem?
IstdasMikadoZielerreicht?
Nein CommitdernderungenindieVersionskontrolle
Ja
ErgibtdienderungSinn?
Ja
Fertig!
Nein
Nein
NchsteVorbedingungauswhlen,umdamitzuarbeiten
DieLsungenalsVorbedingungenimMikadoGraphnotieren
VerwerfenallernderungenmitHilfederVersionskontrolle
Start
Ja
nderung naiv ausfhren.
Herausfinden, was kaputt gegangen ist.Dies sind die Vorbedingungen fr die nderung.
Visualisieren: zum Mikado Graph hinzufgen
REVERT !!!
Wiederholen, bis nichts mehr kaputt geht.
Demo
Uhrzeit wird fortlaufend angezeigt
Uhrzeit wird fortlaufend a