Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

download Wandelbarkeit wieder herstellen - Refactoring C# Legacy Code

of 93

  • date post

    11-Feb-2017
  • Category

    Education

  • view

    161
  • download

    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