Download - Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Transcript
Page 1: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Refactoring C#Legacy Code

Stefan Lieser @StefanLieser

http://refactoring-legacy-code.net

Page 2: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Houston, we’ve had a problem.

http://er.jsc.nasa.gov/seh/13index.jpg

Page 3: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Wandelbarkeit

https://pixabay.com/de/chamäleon-hautnah-exotische-grün-1414084/

Page 4: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Investitionsschutz = Wandelbarkeit

Nicht-funktionale Anforderungen

Funktionale Anforderungen

Page 5: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Das Problem besteht aus drei Teilen:

• Lesbarkeit/Verständlichkeit ist nicht gegeben • Automatisierte Tests fehlen • Komplexe Refactorings sind notwendig

Page 6: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Das Dilemma:

• Um Tests zu ergänzen, => muss refactored werden.

• Um zu refactoren, => müssen Tests ergänzt werden.

Page 7: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

KISS - Keep It Simple Stupid• „Einfach“ in Bezug auf

• Schreiben / Erstellen? • Lesen / Modifizieren !

Page 8: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Ein Sicherheitsnetz spannen:• Versionskontrolle • Continuous Integration • Code Reviews • Automatisierte Tests

Page 9: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Begründung für Refactorings:

• NICHT: Clean Code Developer, Prinzipien,Werte, Craftsmen, …

• Sondern Kundennutzen: • Neues Feature • Bugfix

Page 10: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Es gibt zwei Artenvon Refactorings:

einfache und

komplexe.

Page 11: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Einfache RefactoringsRename Extract Method Introduce VariableIntroduce Parameter etc.

Vollständig werkzeuggestützt durchführbar

Page 12: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Komplexe RefactoringsNicht mehr ausschließlich

werkzeuggestützt durchführbar

Page 13: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Einfache RefactoringsLesbarkeit herstellen.

Erkenntnisse über den Code im Code sichern.

Page 14: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

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

Page 15: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

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

toolgestützt!

Page 16: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

publicIDictionary<string,string>ToDictionary(stringconfiguration){varsettings=SplitIntoSettings(configuration);varpairs=SplitIntoKeyValuePairs(settings);varresult=InsertIntoDictionary(pairs);

returnresult;}

publicIDictionary<string,string>ToDictionary(stringconfiguration){returnInsertIntoDictionary(SplitIntoKeyValuePairs(SplitIntoSettings(configuration)));}

Introduce Variable

Page 17: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

publicIDictionary<string,string>ToDictionary(stringconfiguration){//Splitconfigurationintosettingsvarsettings=configuration.Split(';');

//Splitsettingsintokey/valuepairsvarpairs=newList<KeyValuePair<string,string>>();foreach(varsettinginsettings){varkeyAndValue=setting.Split('=');pairs.Add(newKeyValuePair<string,string>(keyAndValue[0],keyAndValue[1]));}

//Insertpairsintodictionaryvarresult=newDictionary<string,string>();foreach(varpairinpairs){result.Add(pair.Key,pair.Value);}

returnresult;}

Extract Method

Page 18: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

publicIDictionary<string,string>ToDictionary(stringconfiguration){varsettings=SplitIntoSettings(configuration);varpairs=SplitIntoPairs(settings);varresult=InsertIntoDictionary(pairs);returnresult;}

Page 19: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

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,Dictionary<string,string>result){varkey_and_value=setting.Split('=');result.Add(key_and_value[0],key_and_value[1]);}

Page 20: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

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;}

Page 21: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

if(WeckzeitErreicht()){using(varsoundPlayer=newSoundPlayer(@"chimes.wav")){soundPlayer.Play();}timer.Stopp();}

privateboolWeckzeitErreicht(){returnDateTime.Now>=weckZeit;}

Extract Method

if(WeckzeitErreicht()){Wecken();}

Page 22: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

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

Page 23: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

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;}}...}

Page 24: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

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;}

Page 25: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Automatisierte Tests ergänzen

Page 26: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Herausforderungen beim Ergänzen von Tests:• Abhängigkeiten • Aspekte nicht getrennt • Ressourcenzugriffe • Sichtbarkeit • Globaler Zustand

Page 27: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Lösungen:• Zunächst vermehrt Integrationstests,

dann Refactoring und Unit Tests ergänzen. • Mock Frameworks wie

TypeMock Isolator oder Telerik JustMock einsetzen.

Page 28: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Wenige Integrationstests

Viele Unittests

Viele Integrationstests

Wenige Unittests

Page 29: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

usingSystem.Collections.Generic;usingSystem.IO;

namespaceressourcen{publicclassCsvReader{publicIEnumerable<string[]>Read(stringfilename){varlines=File.ReadAllLines(filename);foreach(varlineinlines){varfields=line.Split(';');yieldreturnfields;}}}}

Integrierter Ressourcenzugriff

Page 30: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

[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

Page 31: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

publicclassPrivatesZeugs{privatevoidSaveToDatabase(stringdata){Console.WriteLine("Ressourcenzugriff..."+data);thrownewException();}

publicvoidDoSomething(){Console.WriteLine("DoSomething...");SaveToDatabase("datendatendaten...");}}

Page 32: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

[TestFixture]publicclassPrivateMethoden{[Test]publicvoidPrivate_Methode_nicht_ausführen(){varsut=newPrivatesZeugs();Isolate.NonPublic.WhenCalled(sut,"SaveToDatabase").IgnoreCall();

sut.DoSomething();}}

Verhalten der Attrappe beschreiben

Page 33: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Komplexe RefactoringsMikado Methode

Page 34: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 35: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 36: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 37: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 38: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 39: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 40: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 41: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 42: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 43: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Hm… ist das wirklich clever?

Page 44: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 45: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Cha

Page 46: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Cha

Page 47: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

Cha

Page 48: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Revert!

Change

B

Cha

Page 49: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

Cha

Page 50: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

Cha

C

Page 51: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Revert!

Change

B

D

Cha

C

Page 52: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

Cha

C

Page 53: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

Cha

C

Page 54: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

Cha

C

E

Page 55: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Revert!

Change

B

D

Cha

C

E

Page 56: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

Cha

C

E

Page 57: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

Cha

C

E

Page 58: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

F

Cha

C

E

Page 59: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Revert!

Change

B

D

F

Cha

C

E

Page 60: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

F

Cha

C

E

Page 61: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

F

Cha

C

E

Page 62: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

B

D

F

A

Cha

C

E

Page 63: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Revert!

Change

B

D

F

A

Cha

C

E

Page 64: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 65: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 66: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 67: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 68: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 69: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 70: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 71: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 72: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 73: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 74: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 75: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Change

Page 76: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

✓Change

Page 77: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

✓Change

Page 78: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Hurra!

Page 79: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

B

D

F

A

Change

C

E

Page 80: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

B

D

F

A

Change

C

E

Ana l

y s e

Re f a c t o r i ng

Page 81: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

NachLösungensuchen

DasMikadoZielnotieren

DasZielbzw.dieaktuelleVorbedingungnaivimplementieren

GibteseinProblem?

IstdasMikadoZielerreicht?

Nein CommitderÄnderungenindieVersionskontrolle

Ja

ErgibtdieÄnderungSinn?

Ja

Fertig!

Nein

Nein

NächsteVorbedingungauswählen,umdamitzuarbeiten

DieLösungenalsVorbedingungenimMikadoGraphnotieren

VerwerfenallerÄnderungenmitHilfederVersionskontrolle

Start

Ja

Page 82: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

• Änderung naiv ausführen.

•Herausfinden, was kaputt gegangen ist.Dies sind die Vorbedingungen für die Änderung.

•Visualisieren: zum Mikado Graph hinzufügen

• REVERT !!!

•Wiederholen, bis nichts mehr kaputt geht.

Page 83: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Demo

Page 84: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Uhrzeit wird fortlaufend angezeigt

Page 85: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Uhrzeit wird fortlaufend angezeigt

Eigene Timerklasseerstellen

Page 86: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Uhrzeit wird fortlaufend angezeigt

Eigene Timerklasse integrieren

Eigene Timerklasseerstellen

Page 87: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Uhrzeit wird fortlaufend angezeigt

Anzeigen der Uhrzeit auf eigenem Event

Eigene Timerklasse integrieren

Eigene Timerklasseerstellen

Page 88: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Uhrzeit wird fortlaufend angezeigt

Anzeigen von Uhrzeit und REstzeit trennen

Anzeigen der Uhrzeit auf eigenem Event

Eigene Timerklasse integrieren

Eigene Timerklasseerstellen

Page 89: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Ziel

UnterzielUnterziel

Unterziel

Unterziel

Unterziel

Unterziel

Unterziel

Analyse

Refactoring

Page 90: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Vorteile der Mikado Methode

• Immer auslieferbar • Nur auf dem Trunk, kein Branching • Fokus auf das Ziel; nur das Nötigste tun • Visualisierung

Page 91: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Fazit

• Einfache Refactorings: Lesbarkeit • Automatisierte Tests: Korrektheit • Komplexe Refactorings: Wandelbarkeit

Page 92: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

Fragen?

Page 93: Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

http://refactoring-legacy-code.net

http://linkedin.com/in/stefanlieser

https://twitter.com/StefanLieser

http://xing.com/profile/stefan_lieser