Software Engineering II Refactoring, Style, Software Metriken Prof. Dr. Axel Böttcher 16. Dezember 2012
Wiederholung Technische Schulden geek & poke, http://geek-and-poke.com/2011/12/keynesian-based-coding.html (Creative Commons Attribution 3.0 Unported License)
Wiederholung Technische Schulden geek & poke, http://geek-and-poke.com/2011/12/keynesian-based-coding.html (Creative Commons Attribution 3.0 Unported License)
Wiederholung Technische Schulden geek & poke, http://geek-and-poke.com/2011/12/keynesian-based-coding.html (Creative Commons Attribution 3.0 Unported License)
Refactoring
Was ist hier faul?
Definition Refactoring ist ein Vorgang, der die Struktur von Software verändert, ohne das externe Verhalten (Funktion) zu ändern.
Definition Refactoring ist ein Vorgang, der die Struktur von Software verändert, ohne das externe Verhalten (Funktion) zu ändern. Mühevolle Detailarbeit (minimalinvasiv).
Definition Refactoring ist ein Vorgang, der die Struktur von Software verändert, ohne das externe Verhalten (Funktion) zu ändern. Mühevolle Detailarbeit (minimalinvasiv). Grundlage sind Unit Tests, mit denen kontinuierlich die Funkion überprüft werden kann.
Definition Refactoring ist ein Vorgang, der die Struktur von Software verändert, ohne das externe Verhalten (Funktion) zu ändern. Mühevolle Detailarbeit (minimalinvasiv). Grundlage sind Unit Tests, mit denen kontinuierlich die Funkion überprüft werden kann. Ausgangspunkt sind üble Gerüche (smells), für die eine gewisse Sensibilität erforderlich ist.
Definition Refactoring ist ein Vorgang, der die Struktur von Software verändert, ohne das externe Verhalten (Funktion) zu ändern. Mühevolle Detailarbeit (minimalinvasiv). Grundlage sind Unit Tests, mit denen kontinuierlich die Funkion überprüft werden kann. Ausgangspunkt sind üble Gerüche (smells), für die eine gewisse Sensibilität erforderlich ist. Es gibt Standard-Techniken, die teilweise von IDEs unterstützt werden. Eine gute Übersicht steht unter http://www.refactoring.com/catalog
Extremfall Wie refaktorisiert man solche Software:
Ausgabe
Code Smells Eine Übersicht Code-Duplizierung Lange Methode Große Klasse Klasse zu umfangreich Faule Klasse Klasse leistet zu wenig Lange Parameterliste mehr Parameter übergeben als nötig Nichtssagender Name Kommentare Wo notwendig, ist häufig der Code schlecht... Eine umfangreiche Liste weiterer Smells: http://www.codinghorror.com/blog/2006/05/code-smells.html
Duplicated Code Der absolute Klassiker! Abhilfe: Methode extrahieren Klasse extrahieren Methode nach oben (d.h. in Basisklasse) verschieben Template-Methode bilden
Duplicated Code: Extract Method
Schlechte, nichtssagende Namen Abhilfe: Umbenennen (Rename) Gute Namen sind sehr wichtig Mit fortschreitender Arbeit ändern sich Namen und werden angepasst Umbenennung kann gut automatisiert werden; ist ein syntax-sensitiver Vorgang Automatisierung klappt auch Klassenübergreifend
Kommentare geek & poke, http://geek-and-poke.com/2012/04/sometimes-its-that-simple.html (Creative Commons Attribution 3.0 Unported License)
Kommentare geek & poke, http://geek-and-poke.com/2012/04/sometimes-its-that-simple.html (Creative Commons Attribution 3.0 Unported License)
Kommentare geek & poke, http://geek-and-poke.com/2012/04/sometimes-its-that-simple.html (Creative Commons Attribution 3.0 Unported License)
Code Smells Kommentare (I) 1 private int a = 0; // a wird mit 0 i n i t i a l i s i e r t Der Kommentar beschreibt exakt, was auch im Code steht Es benötigt mehr Zeit, den Kommentar zu lesen als den Code Redundanter Kommentar entfernt 1 private int a = 0;
Code Smells Kommentare (II) 1 private int i ; // Enthaelt den Fehlercode Hier erklärt der Kommentar die Bedeutung der Variablen i Doppelter Code Smell: Nichtssagender Name + unötiger Kommentar Aussagekräftige Variable verwendet 1 private int errorcode ;
Beispiel Beispiel aus java.lang. Integer. tostring (): 1 // I use the i n v a r i a n t d i v i s i o n by m u l t i p l i c a t i o n t r i c k to 2 // a c c e l e rate Integer. tostring. In p a r t i c u l a r we want to 3 // avoid d i v i s i o n by 10. 4 // 5 // The t r i c k has roughly the same performance c h a r a c t e r i s t i 6 // as the c l a s s i c Integer. tostring code on a non JIT VM. 7 // The t r i c k avoids. rem and. div c a l l s but has a longer code 8 // path and i s thus dominated by dispatch overhead. In the 9 // JIT case the dispatch overhead doesn t e x i s t and the 10 // t r i c k i s considerably f a s t e r than the c l a s s i c code. 11 // 12 // TODO FIXME : convert ( x 52429) into the equiv s h ift add 13 // sequence. 14 // 15 // RE: Division by I n varia nt Integers using M u l t i p l i c a t i o n 16 // T Gralund, P Montgomery 17 // ACM PLDI 1994 18 //
Lange Methode Nachteile langer Methoden: lassen sich schwer testen erledigen häufig verschiedene Dinge es lassen sich schwer spezialisierte Subklassen bilden sind potenzielle Quellen für Fehler, da wenig transparent
Code Smell Lange Methode 1 (I) 1 public void debit ( fl o at amount ) { 2 // deduct amount from balance 3 balance = amount ; 4 5 // record transaction 6 transactions. add (new Transaction ( true, amount ) ) ; 7 8 // update l a s t debit date 9 Calendar calendar = Calendar. getinstance ( ) ; 10 11 lastdebitdate = calendar. get ( Calendar.DATE) + / + 12 calendar. get ( Calendar.MONTH) + / + 13 calendar. get ( Calendar.YEAR ) ; 14 } Extract Method dreifach 1 Code smell of the week, http://www.youtube.com/watch?v=u4hipntxwyc
Code Smell Lange Methode (II) 1 public void debit ( fl o at amount ) { 2 deductamountfrombalance ( amount ) ; 3 recordtransaction (amount, true ) ; 4 updatelastdebitdate ( ) ; 5 } 6 7 private void deductamountfrombalance ( f loat amount ) { 8 balance = amount ; 9 } 10 11 private void recordtransaction ( f loat amount, boolean isdebit ) { 12 transactions. add (new Transaction ( isdebit, amount ) ) ; 13 } 14 15 private void updatelastdebitdate () { 16 Calendar calendar = Calendar. getinstance ( ) ; 17 18 lastdebitdate = calendar. get ( Calendar.DATE) + / + 19 calendar. get ( Calendar.MONTH) + / + 20 calendar. get ( Calendar.YEAR ) ; 21 }
Magic Numbers 1 mins = t o t a l /60; 2 secs = t o t a l 60 mins ; 3 4 hours = mins /60; 5 mins = mins 60 hours ; 6 7 days = hours /24; 8 hours = hours 24 days ; Abhilfe: durch static final Konstante ersetzen (Extract constant):
Änderungen am API Änderungen an öffentlichen Methoden eines API sind dann gefährlich, wenn bereits Kunden dieses API verwenden. Besser: deprecaten.
Änderungen am API Änderungen an öffentlichen Methoden eines API sind dann gefährlich, wenn bereits Kunden dieses API verwenden. Besser: deprecaten. Ansonsten: Kein Problem ein API zu verbessern.
Weitere Smells Smell: Datenklassen (mangelnde Kapselung) Feld Kapseln: Public Felder mit Getter und setter kapseln (diese können beispielsweis ein Eclipse eutomatisch erzeugt werden)
Siehe http://worsethanfailure.com 1 public int hightemp ( int Temp1, i nt Temp2, int Temp3, int Temp4, 2 i nt Temp5) { 3 int HighTemp = 0; 4 i f ((Temp1 >= Temp2) && (Temp1 >= Temp3) && (Temp1 >= Temp4) && (Temp1 >= Temp5)) 5 HighTemp = Temp1; 6 else i f ((Temp2 >= Temp1) && (Temp2 >= Temp3) && (Temp2 >= Temp4) && (Temp2 >= Temp5 7 HighTemp = Temp2; 8 else i f ((Temp3 >= Temp1) && (Temp3 >= Temp2) && (Temp3 >= Temp4) && (Temp3 >= Temp5 9 HighTemp = Temp3; 10 else i f ((Temp4 >= Temp1) && (Temp4 >= Temp3) && (Temp4 >= Temp2) && (Temp4 >= Temp5 11 HighTemp = Temp4; 12 else i f ((Temp5 >= Temp1) && (Temp5 >= Temp3) && (Temp5 >= Temp4) && (Temp5 >= Temp2 13 HighTemp = Temp5; 14 return HighTemp ; 15 }
Frage Welcher der folgenden Begriffe beschreibt keinen Code Smell? 1. Code-Duplizierung 2. Rename 3. Lange Methode 4. Magic Number
Smell Detection: Programmierstil Code Guarding Wartbarkeit
Wartbarkeit ist ein nicht-funktionales Qualitätsmerkmal der Software(-Architektur). Dieser Abschnitt beschäftigt sich damit, welche Hilfsmittel es gibt, Smells automatisiert zu entdecken Smells schon beim Entwickeln zu vermeiden verstehbaren Code zu schreiben wartbaren Code zu schreiben
Stil-Richtlinien Stell dir vor es gibt Programmierrichtlinien und keiner hält sich dran. Viele Firmen nutzen interne Richtlinien für den Programmierstil. Bekanntester (wahrscheinlich) Style Guide:
Vorteile Vermindert die Fehleranfälligkeit. Verbessert die Lesbarkeit. Erleichtert das Verständnis fremden Codes. Beispiel: Was stimmt an folgendem C-Programm nicht? void calc(int *i, int*j) { *i = *i/*j; }
IDE-Integration Es gibt Eclipse-Plugins, um den Programmierstil zu überprüfen, z.b.: (Checkstyle) Das Tool ist weitgehend konfigurierbar:
Fehlermuster Neben Entwurfsmustern (Design Patterns) gibt es typische Fehlermuster (Bug Patterns). Echtes Beispiel aus dem Eclipse Code der Verion 3.0 (siehe findbugs.sf.net): 1 i f ( in == null ) 2 try { 3 in. close ( ) ; 4 } Alleine das Verständnis der Bug Patterns bringt großen Erkenntnisgewinn. Teilweise nahe Verwandtschaft zwischen Bug Patterns und Style Rules.
Fehlerkategorien Korrektheit auch im Hinblick auf Multithreading. Z.B. String- oder Objektvergleich durch == und!=. Angreifbarkeit durch Schadprogramme. Z.B. durch Rückgabe von Referenzen auf interne Arrays. Effizienz und Design.
Weitere Beispiele if(name!= null name.length > 0) log(x.y()); if(x == null) throw new XyzException("x is null"); public Object execute(handler h, Node n){ return execute(h, n); }
boolean equals(object o){ Frame f = (Frame)o; return f.getx() == this.x; } C d; if(d instanceof D)...
if ( (str.charat(i)>57 str.charat(i)<48) &&!(str.charat(i)==. ) )
Eclipse Findbugs Plugin Integriert sich nahtlos in den Mechanismus zum Anzeigen von Fehlern: Je entdecktem potenziellem Bug wird eine Warnung angezeigt:
Software Metriken
Software Metriken warum und wozu Wer misst, misst Mist Grundsätzliche Schwierigkeit, die Komplexität eines Stück Programms durch eine Zahl (oder mehrere Zahlen) zu messen. Das klassische Maß ist Lines of Code (LOC); dieses ist allerdings wenig aussagekräftig.
Cyclomatic Complexity, kurz CC Thomas McCabe, 1976 Sprach- und formatunabhängiges Ist ein Maß für die voneinander unabhängigen Pfade durch ein Programmmodul. Ausgangspunkt: Der Kontrollfluss eines Programms kann durch einen gerichteten Graphen dargestellt werden. Zur Erinnerung: Ein gerichteter Graph besteht aus Knoten (node) und gerichteten Kanten (edge, arc). dabei: Cyclomatic complexity (CC) = E N + 2 E: Anzahl der Kanten des Graphen N: Anzahl der Knoten des Graphen
Beispiel für CC=2 1 public Singleton getinstance (){ 2 i f ( theinstance == null ) 3 theinstance = new Singleton ( ) ; 4 return theinstance ; 5 }
Beispiel für CC=4 1 public void execcommand( int cmd){ 2 switch (cmd) 3 { 4 case 0: sendcommand (LEFTREV) ; break ; 5 case 1: sendcommand (LEFTSTOP) ; break ; 6 c a se 2: sendcommand (LEFTFWD) ; break ; 7 default : sendcommand (EMERGENCYSTOP) ; br 8 } 9 return ; 10 }
Programme mit niedriger zyklomatischer Komplexität sind leichter zu lesen, zu warten und zu testen. Studien belegen einen Zusammenhang zwischen zyklomatischer Komplexität und der Fehleranfälligkeit von Code.: Cyclomatic Complexity Risiko 1-10 einfaches Programm, kaum Risiko 11-20 etwas komplexer, moderates Risiko 21-50 komplexes Programm mit hohem Risiko > 50 nicht mehr testbar: Sehr hohes Risiko
Tools zur Bestimmung der CC
Stabilität Abstractness (Generality) A eines Pakets p: A = Anzahl abstrakter Klassen im Paket Gesamtzahl Klassen im Paket Wertebereich 0 (vollständig konkretes Paket) bis 1 (vollständig abstraktes Paket).
Stabilität Abstractness (Generality) A eines Pakets p: A = Anzahl abstrakter Klassen im Paket Gesamtzahl Klassen im Paket Wertebereich 0 (vollständig konkretes Paket) bis 1 (vollständig abstraktes Paket). Afferent Couplings C a eines Pakets p: Die Anzahl an Paketen außerhalb des Pakets p, die von Klassen innerhalb des Pakets abhängen (Vererbung, Assoziation). Efferent Couplings C e eines Pakets p: Die Anzahl an Klassen außerhalb des Pakets, von denen Klassen innerhalb des Pakets p abhängen.
Stabilität Abstractness (Generality) A eines Pakets p: A = Anzahl abstrakter Klassen im Paket Gesamtzahl Klassen im Paket Wertebereich 0 (vollständig konkretes Paket) bis 1 (vollständig abstraktes Paket). Afferent Couplings C a eines Pakets p: Die Anzahl an Paketen außerhalb des Pakets p, die von Klassen innerhalb des Pakets abhängen (Vererbung, Assoziation). Efferent Couplings C e eines Pakets p: Die Anzahl an Klassen außerhalb des Pakets, von denen Klassen innerhalb des Pakets p abhängen. Instability I: I = C a + C e Wertebereich 0... 1. Paket mit I = 0 ist maximal stabil und mit I = 1 maximal unstabil. C e
) > I J H =? J A I I 1 I J = > E E J O K J I A H * A H A E? D 0 = K F J E E A I? D A H D = B J A H * A H A E? D
Frage Folgende Informationen liegen Ihnen zur Stabilität Ihres Codes vor: C e = 3, C a = 0, Ihr Paket besteht aus 100 Klassen, davon 30 abstrakt. Welcher Punkt im Diagramm beschreibt am besten die Stabilität Ihres Codes?
Frage Folgender Kontrollfluss eines Programms ist gegeben. Wie hoch ist das Risiko für die Fehleranfälligkeit des Codes? 1. Kaum Risiko 2. Moderates Risiko 3. Hohes Risiko 4. Sehr hohes Risiko