76
Software Evolution LEGACY CODE REFAKTORISIEREN

Legacy Code refaktorisieren

Embed Size (px)

Citation preview

Page 1: Legacy Code refaktorisieren

Software Evolution LEGACY CODE REFAKTORISIEREN

Page 2: Legacy Code refaktorisieren

Der Sprecher

Hendrik LöschSenior Consultant & [email protected]@HerrLoeschJust-About.Net

Page 3: Legacy Code refaktorisieren

WAS IST SOFTWARE EVOLUTIONRefactoring

Page 4: Legacy Code refaktorisieren

Refactoring

Quelle: Ron Jeffries http://xprogramming.com/articles/refactoring-not-on-the-backlog

Page 5: Legacy Code refaktorisieren

Evolutionäre Softwareentwicklung

Entwicklung

Weiter-entwicklung

Weiter-entwicklung

Weiter-entwicklung

StabilisierungOptimierung

Sanierung

StabilisierungStabilisierung

Optimierung

t in Jahren

Page 6: Legacy Code refaktorisieren

Der Softwarelebenszyklus

Quelle: Software Evolution, Tom Mens, Serge Demeyer

Initial Development Phase Out Close DownEvolution

changes patches

Servicing

Page 7: Legacy Code refaktorisieren

REFAKTORISIEREN

!!!!1!1!!!

Die Codequalität

ist schlecht.

Wir brauchen mehr

Automatisierte

Tests!

Wir brauchen Features.Mehr Features!FEATURES!!1!1!11!!!!!

Page 8: Legacy Code refaktorisieren

Debugging at its best

Page 9: Legacy Code refaktorisieren

Formen der Evolution

Big Bang

„Partialy New“

Maintenance

• Das Softwaresystem wird (nahezu) komplett neu entwickelt.

• Das Altsystem muss während dessen noch betreut werden.

• Es werden Teile neu entwickelt oder hinzugefügt.

• Fokussierung auf Bugfixes und Stabilisierung.

Page 10: Legacy Code refaktorisieren

1. It is hard to change because every change affects too many other parts of the system. (Rigidity - Starr)

2. When you make a change, unexpected parts of the system break. (Fragility - Zerbrechlich)

3. It is hard to reuse in another application because it cannot be disentangled from the current application. (Immobility - Unbeweglich)

Quelle: http://www.objectmentor.com/resources/articles/dip.pdf

Was ist schlechtes Design?!?

Page 11: Legacy Code refaktorisieren

Technische Schuld

Quelle: Martin Fowler http://martinfowler.com/bliki/TechnicalDebtQuadrant.html

Ward Cunningham

rücksichtslos umsichtig

überlegtversehentlich

„Wir haben keine Zeit für ein

Design.“

„Wir müssen jetzt liefern und uns später um die Konsequenzen

kümmern.“

„Jetzt wissen wir wie wir es hätten machen

müssen.“

„Layer? Was ist das?“

Page 12: Legacy Code refaktorisieren

Was ist gutes Design?!?

Quelle: http://martinfowler.com/bliki/BeckDesignRules.html

Page 13: Legacy Code refaktorisieren

Horseshoe Process Model

Legacy Software System

Improved restructured

model

New Software System

High-level architectural

model

analyse Improve,restructure,

extend

understand,extract,abstract

generate,refine

Reve

rse

Engi

neer

ing

Forward Engineering

Restructuring

Page 14: Legacy Code refaktorisieren

Refactoring

Refactoring bezeichnet in der Software-Entwicklung die manuelle oder automatisierte Strukturverbesserung von Quelltexten unter Beibehaltung des beobachtbaren Programmverhaltens.

Dabei sollen die Lesbarkeit, Verständlichkeit, Wartbarkeit und Erweiterbarkeit verbessert werden, mit dem Ziel, den jeweiligen Aufwand für Fehleranalyse und funktionale Erweiterungen deutlich zu senken.

Quelle: https://de.wikipedia.org/wiki/Refactoring

Page 15: Legacy Code refaktorisieren

Schreibe nur Code, der verlangt wird.

Entwickle schrittweise Deinen Code.

Wähle möglichst kleine Schritte.

Jeder Schritt muss den Code verbessern.

Test

Implementierung

Refaktorisierung

Page 16: Legacy Code refaktorisieren

ALLES WIRD GUTRefactoring

Page 17: Legacy Code refaktorisieren

Das Vorgehen für uns Entwickler

1. Änderungsbereiche aufdecken. 2. Testpunkte aufdecken. 3. Abhängigkeiten aufbrechen. 4. Tests schreiben. 5. Änderungen vornehmen & refaktorisieren.

Page 18: Legacy Code refaktorisieren

Wie vorgehen?

1. Quellcode einchecken.2. Einen Überblick darüber verschaffen was geändert werden

soll.3. Mindestens einen Test schreiben der das bestehende

Verhalten charakterisiert.

4. Refaktorisieren

5. Prüfen ob das Verhalten sich verändert hat.

Page 19: Legacy Code refaktorisieren

Tests als Grundlage

Code without tests is bad code. It doesn't matter how well written it is; it doesn't matter how pretty or object-oriented or well-encapsulated it is.

With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse.

Page 20: Legacy Code refaktorisieren

Benutzersicht

Speichern

Datenvalidierung

Fehlerbehandlung

Validierungsregeln

Speichervorgang

Zurücksetzen bei Fehler

Fehlerausschrift

Validator

Event Handling

DBDatenbank-anbindung

DS

Extern

Typische Testarten

Entwicklersicht

Page 21: Legacy Code refaktorisieren

Benutzersicht Entwicklersicht

Speichern

Datenvalidierung

Fehlerbehandlung

Validierungsregeln

Speichervorgang

Zurücksetzen bei Fehler

Fehlerausschrift

Validator

Unit-Tests

System-Tests

Event Handling

DBDatenbank-anbindung

DS

Extern

Typische Testarten

Integration Tests

Page 22: Legacy Code refaktorisieren

Benutzersicht Entwicklersicht

Speichern

Datenvalidierung

Fehlerbehandlung

Validierungsregeln

Speichervorgang

Zurücksetzen bei Fehler

Fehlerausschrift

Validator

Micro Test

Component Tests

Event Handling

DBDatenbank-anbindung

DS

Extern

Unit Test != Unit Test

Page 23: Legacy Code refaktorisieren

Benutzersicht Entwicklersicht

Speichern

Datenvalidierung

Fehlerbehandlung

Validierungsregeln

Speichervorgang

Zurücksetzen bei Fehler

Fehlerausschrift

Validator

Micro Test

Event Handling

DBDatenbank-anbindung

DS

Extern

Baby Step Refactoring

Page 24: Legacy Code refaktorisieren

Benutzersicht Entwicklersicht

Speichern

Datenvalidierung

Fehlerbehandlung

Validierungsregeln

Speichervorgang

Zurücksetzen bei Fehler

Fehlerausschrift

Validator

Component Tests

Event Handling

DBDatenbank-anbindung

DS

Extern

Component Based Refactoring

Page 25: Legacy Code refaktorisieren

Benutzersicht

Speichern

System-Tests

ZeugDB

DS

Extern

Integration Based Refactoring

Integration Tests

Page 26: Legacy Code refaktorisieren

Keine Änderung ohne Test

Quelle: Daniel Marbach, Legacy Code und Jetzt?

Page 27: Legacy Code refaktorisieren

UND WIE REFAKTORISIERE ICH

NUN WIRKLICH?

Refactoring

Page 28: Legacy Code refaktorisieren

Refactoring ArtenAdd Parameter Introduce Class Annotation Replace Conditional with PolymorphismChange Bidirectional Association to Unidirectional Introduce Expression Builder Replace Constructor with Factory MethodChange Reference to Value Introduce Foreign Method Replace Data Value with ObjectChange Unidirectional Association to Bidirectional Introduce Gateway Replace Delegation With HierarchyChange Value to Reference Introduce Local Extension Replace Delegation with InheritanceCollapse Hierarchy Introduce Named Parameter Replace Dynamic Receptor with Dynamic Method DefinitionConsolidate Conditional Expression Introduce Null Object Replace Error Code with ExceptionConsolidate Duplicate Conditional Fragments Introduce Parameter Object Replace Exception with TestDecompose Conditional Isolate Dynamic Receptor Replace Hash with ObjectDuplicate Observed Data Lazily Initialized Attribute Replace Inheritance with DelegationDynamic Method Definition Move Eval from Runtime to Parse Time Replace Loop with Collection Closure MethodEagerly Initialized Attribute Move Field Replace Magic Number with Symbolic ConstantEncapsulate Collection Move Method Replace Method with Method ObjectEncapsulate Downcast Parameterize Method Replace Nested Conditional with Guard ClausesEncapsulate Field Preserve Whole Object Replace Parameter with Explicit MethodsExtract Class Pull Up Constructor Body Replace Parameter with MethodExtract Interface Pull Up Field Replace Record with Data ClassExtract Method Pull Up Method Replace Subclass with FieldsExtract Module Push Down Field Replace Temp with ChainExtract Subclass Push Down Method Replace Temp with QueryExtract Superclass Recompose Conditional Replace Type Code with ClassExtract Surrounding Method Remove Assignments to Parameters Replace Type Code with Module ExtensionExtract Variable Remove Control Flag Replace Type Code With PolymorphismForm Template Method Remove Middle Man Replace Type Code with State/StrategyHide Delegate Remove Named Parameter Replace Type Code with SubclassesHide Method Remove Parameter Self Encapsulate FieldInline Class Remove Setting Method Separate Query from ModifierInline Method Remove Unused Default Parameter Split Temporary VariableInline Module Rename Method Substitute AlgorithmInline Temp Replace Abstract Superclass with ModuleIntroduce Assertion Replace Array with Object

refactoring.com

Page 29: Legacy Code refaktorisieren

Dinge testbar gestalten

Seam A seam is a Place where you can alter behavior in your program without editing in that Place.

Enabling Point Every seam has an enabling point, a Place where you can make the decision to use one behavior or another.

Page 30: Legacy Code refaktorisieren

Echte Abstraktion statt bloßer Indirektion

Page 31: Legacy Code refaktorisieren

Code Transformation

Code <-> MethodeMethode <-> MethodenMethode(n) <-> KlasseKlasse <-> KlassenKlasse(n) <-> InterfaceInterface <-> InterfacesKlasse(n) <-> Namspace(s)Namspace(s) <-> Assembly/ies

Inline extract

Page 32: Legacy Code refaktorisieren

X-Schichten Modell

User Interface

Business Layer

Data Access Layer

Cross Cutting

Concerns

Page 33: Legacy Code refaktorisieren

DEMO

Page 34: Legacy Code refaktorisieren

Abhängigkeiten durchbrechenpublic class PersonSelectionViewModel{

List<Person> Persons { get; set; }public void Initialize(){

var query = "SELECT * FROM PEOPLE";var connection = new DataBaseConnection(); var command = connection.CreateCommand(query);var reader = command.Excute();

this.Persons = new List<Person>();while(reader.Next()){

var person = new Person(); person.Id = reader["ID"]; person.Name = reader["NAME"];

this.Persons.Add(person);}

return;}. . .

Page 35: Legacy Code refaktorisieren

Integrationstest

[TestMethod]public void ViewModelMustShowAllPersons (){ this.SetupDataBase(); var person = this.AddPersonToDataBase();

var sut = new PersonSelectionViewModel(); sut.Initialize();

this.AssertThatDataBaseContains(person);}

Page 36: Legacy Code refaktorisieren

Abhängigkeiten durchbrechenpublic class PersonSelectionViewModel{

List<Person> Persons { get; set; }

public void Initialize(){

var repository = new PersonRepository();this.Persons = repository.GetAllPersons();return;

}

public void Save(Person person){

var repository = new PersonRepository();repository.Save(person);return;

}

. . .}

Page 37: Legacy Code refaktorisieren

Shims

[TestMethod]public void ViewModelMustShowAllPersons(){

using(ShimsContext.Create()){

var person = new Person() { Name = "Meyer" };ShimPersonRepository.AllInstances.GetPersons =

() => return new List<Person> {person};

var sut = new PersonSelectionViewModel();sut.Initialize();

Assert.IsTrue(sut.Persons.Contains(person));}

}

Page 38: Legacy Code refaktorisieren

JustMock

[TestMethod]public void ViewModelMustShowAllPersons (){ var person = new Person() { Name = "Meyer" };

var rep = Mock.Create<PersonRepository>(); Mock.Arrange(() => rep.Persons).IgnoreInstance()

.Returns(new List<Person> { person });

var sut = new PersonSelectionViewModel();

// Initializes the view model sut.Initialize(); Assert.IsTrue(sut.Persons.Contains(person));}

Page 39: Legacy Code refaktorisieren

Abhängigkeiten durchbrechenpublic class PersonSelectionViewModel{

List<Person> Persons { get; set; }private IPersonRepository repository;

private IPersonRepository Repository {

get {

return this.repository ?? (this.repository = new PersonRepository());}

}

public void Initialize(){

this.Persons = this.repository.GetAllPersons();return;

}

. . .}

Page 40: Legacy Code refaktorisieren

Private Object & FakeItEasy

[TestMethod]public void ViewModelMustShowAllPersons(){

var person = new Person() { Name = "Meyer" };

var repository = A.Fake<IPersonRepository>();A.CallTo(repository.GetPersons()).Returns(new List<Person> {person});

var sut = new PersonSelectionViewModel();

var accessor = new PrivateObject(sut);accessor.SetFieldOrProperty("repository", repository);

sut.Initialize();

Assert.IsTrue(sut.Persons.Contains(person));}

Page 41: Legacy Code refaktorisieren

REFAKTORISIERUNGSPOTENTIALE AUFDECKEN

Refactoring

Page 42: Legacy Code refaktorisieren

FX Cop

Statische Analyse von kompiliertem Code mit Einbindung in den Build

Adressiert:• Performance• Sicherheit• Namensgebung• Interoperabilität• …

Page 43: Legacy Code refaktorisieren

Code Metriken ermitteln

Page 44: Legacy Code refaktorisieren

Lines of Code

SLOC(in Mio.)

Windows NT 3.1 (1993) 4–5Windows NT 3.5 (1994) 7–8Windows NT 4.0 (1996) 11–12Windows 2000 (2000) > 29Windows XP (2001) 40Windows Server 2003 (2003) 50

Page 45: Legacy Code refaktorisieren

Lines of Code

= 187 LoC

π •👍Klasse < 500 LoC

Methode < 50 LoC ?

Page 46: Legacy Code refaktorisieren

Class Coupling

1 2 3

π •👍Methode <= 9

Page 47: Legacy Code refaktorisieren

Class Coupling

Page 48: Legacy Code refaktorisieren

Depth of Inheritance

Page 49: Legacy Code refaktorisieren

Depth of Inheritance

π •👍Hierachieebene <= 6

public class ActionBarViewModel : ReactiveObject, IActionBarViewModel

Page 50: Legacy Code refaktorisieren

Cyclomatic Complexity

Page 51: Legacy Code refaktorisieren

Cyclomatic Complexity

Komplexität == 2

Page 52: Legacy Code refaktorisieren

Cyclomatic Complexity

Komplexität == 3

public SourceSelectionViewModel(IEventAggregator eventAggregator) { this.eventAggregator = eventAggregator;

this.readDataCommand = new ReactiveCommand(); this.readDataCommand.Subscribe( _ => this.ReadData()); }

Page 53: Legacy Code refaktorisieren

Cyclomatic Complexity

π •👍1 bis 10 = normal11 bis 20 = moderat21 bis 50 = riskant> 50 = instabil

Page 54: Legacy Code refaktorisieren

Code Coverage aka Test Coverage

Page 55: Legacy Code refaktorisieren

Code Coverage aka Test Coverage

Komplexität == 3

Page 56: Legacy Code refaktorisieren

Maintainability Index

20 bis 1009 bis 200 bis 9

Maintainability Index = MAX(0,(171 - 5.2 * log(Halstead Volume)

- 0.23 * (Cyclomatic Complexity) - 16.2 * log(Lines of Code))*100 / 171)

Page 57: Legacy Code refaktorisieren

Code Clones

Page 58: Legacy Code refaktorisieren

Architektur

Page 59: Legacy Code refaktorisieren

Code Map

Page 60: Legacy Code refaktorisieren

Code Map

Page 61: Legacy Code refaktorisieren

Abhängigkeitsgraph

Page 62: Legacy Code refaktorisieren

Abhängigkeitsgraph

Page 63: Legacy Code refaktorisieren

Abhängigkeitsgraph

Page 64: Legacy Code refaktorisieren

Abhängigkeitsgraph

Page 65: Legacy Code refaktorisieren

NDepend

Page 66: Legacy Code refaktorisieren

NDepend V6

Page 67: Legacy Code refaktorisieren

C.R.A.P.

“The C.R.A.P. (Change Risk Analysis and Predictions) index is designed to analyze and predict the amount of effort, pain, and time required to maintain an existing body of code.”

Alberto Savoia

Quelle: http://www.artima.com/weblogs/viewpost.jsp?thread=210575

C.R.A.P.(m) = CC(m)^2 * (1 – Coverage(m)/100)^3 + CC(m)

Page 68: Legacy Code refaktorisieren

C.R.A.P.

Quelle: https://www.artima.com/weblogs/viewpost.jsp?thread=215899

C.R.A.P.(m) = CC(m)^2 * (1 – Coverage(m)/100)^3 + CC(m)

π •👍30

Method’s CC

% of coverage required to be below CRAPpy threshold

0 – 5

0%

10

42%

15 57%20 71%25 80%30

100%

31+

No amount of testing will keep methods this complex out of CRAP territory.

Page 69: Legacy Code refaktorisieren

FAZITRefactoring

Page 70: Legacy Code refaktorisieren

Systemtest

Integrationstest

Unit Tests

Testautomatisierungspyramide

Page 71: Legacy Code refaktorisieren

Testvorgehen

Integration Based

Component Based

Baby StepUI Based

Page 72: Legacy Code refaktorisieren

Refactoring

Quelle: Ron Jeffries http://xprogramming.com/articles/refactoring-not-on-the-backlog

Page 73: Legacy Code refaktorisieren

Wieviel ist gut für mich?

Quelle: Dr. Elmar Jürgens, CQSE

43

2

1

Keine Defizite

Keine Defizite in geändertem Code

Keine neuen Defizite

Egal

Page 74: Legacy Code refaktorisieren

Pragmatismus vs. Overengineering

Pragmatismus Overengineering

Page 75: Legacy Code refaktorisieren

Pro-Tipp

Refaktorisiere niemals nur des Refaktorisierens wegen!

Page 76: Legacy Code refaktorisieren

Der Sprecher

Hendrik LöschSenior Consultant & [email protected]@HerrLoeschJust-About.Net