$KK(Methoden) $KKK(Extract Method) $KKKK(Einführung) $R(Source) void myMethod(){ if (widgetId == #F0F898){ "ist Widget sichtbar" tueEtwas(); } $R\ daraus wird: $R(Source) boolean isWidgetVisible(){ return widgetId == #F0F898 } void myMethod(){ if (isWidgetVisisble){ tueEtwas(); } } $R\ Was ist passiert? * Die Methode myMethod wurde extrahiert. Eine komplexe Methode wurde in einfachere Methoden aufgeteilt. * Ein Kommentar wurde in einfachere Methoden mit einem aussagekräftigen Namen ersetzt $R(Regel) Do not comment bad code, rewrite it $R\ $KKKK(Idealer Design von Methoden) * Eine Methode sollte jeweils nur eine Aufgabe erfüllen * Die Methoden sollte kurz sein * Komplexerer Methoden rufen Methoden der naechst niedrigen Abstraktionsstufe auf. * Methoden der obersten Abstraktionsstufe sind oeffentlich. * Methoden der untersten Abstraktionsstufe sind Accessor-Methoden * Wichtig sind aussagekräftige Namen $KKKK(Vorteile kurzer Methoden) * Hoehere Wahrscheinlichkeit der Wiederverwendung * Leichter zu warten * effektivere Coverageanalyse Bemerkung: Ein Coverageanalyse-Tool ist ein unentbehrliches Hilfsmittel für Tests. Es stellt fest, welche Klassen bzw. Methoden, wie oft und ggf. mit welchen Parametern aufgerufen worden sind. So kann man feststellen, welcher Bereich des Source Codes noch nicht ausreichend getestet worden ist. Einfache Coverageanalyse - Tools erkennen nicht welche Verzweigungen (z.B IFThenElse Block) innerhalb einer Methode aufgerufen worden ist. Deshalb verwende kleine Methoden, ohne groessere Verzweigungen $R(Literatur) [e|Reusability through self encapsulation>.\dokumente\self-enc.htm] Mein Lieblingsartikel aus dem Plop1 Buch von Ken Auer. Es geht um grundsaetzliche Vorgehensweise beim Design einer Klasse $R\ Extrahiere Methode ohne lokale Variable $R(Source) void printOwing(){ Enumeration e = _orders.elements(); double outstanding = 0.0 ; // print banner System.out.println ("*************************"); System.out.println ("***** Customer Owes *****"); System.out.println ("*************************"); // calculate outstanding while (e.hasMoreElements()){ Order each = (Order) e.nextElement(); outstanding += each.getAccount(); } // printDetails System.out.println("name:" + _name); System.out.println("amount:" + outstanding); } $R\ Zuerst wird printBanner extrahiert, da in diesem Teil keine lokalen Variablen sind, ist diese Aktion besonders trivial. $R(Source) void printOwing(){ Enumeration e = _orders.elements(); double outstanding = 0.0 ; printBanner(); // calculate outstanding while (e.hasMoreElements()){ Order each = (Order) e.nextElement(); outstanding += each.getAccount(); } // printDetails System.out.println("name:" + _name); System.out.println("amount:" + outstanding); } void printBanner(){ System.out.println ("*************************"); System.out.println ("***** Customer Owes *****"); System.out.println ("*************************"); } $R\ Extrahiere Methode mit lokale Variablen Jetzt wird printDetails extrahiert. Dabei wird eine lokale Variable outstanding benutzt, die als Parameter übergeben wird $R(Source) void printOwing(){ Enumeration e = _orders.elements(); double outstanding = 0.0 ; printBanner(); // calculate outstanding while (e.hasMoreElements()){ Order each = (Order) e.nextElement(); outstanding += each.getAccount(); } printDetails(outstanding) } void printBanner(){ System.out.println ("*************************"); System.out.println ("***** Customer Owes *****"); System.out.println ("*************************"); } void printDetails( double outstanding ){ System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); } $R\ Jetzt wird calculate outstanding extrahiert. Die lokale Variable outstanding kann in extrahierten Teil definiert werden. Da sie von printDetails noch gebraucht wird, muss sie als returnWert zurückgegeben werden $R(Source) void printOwing(){ printBanner(); double outstanding = getOutstanding(); printDetails(outstanding) } void printBanner(){ System.out.println ("*************************"); System.out.println ("***** Customer Owes *****"); System.out.println ("*************************"); } void printDetails( double outstanding ){ System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); } double getOutstanding(){ Enumeration e = _orders.elements(); double result = 0.0; while (e.hasMoreElements()){ Order each = (Order) e.nextElement(); result += each.getAmount(); } return result ; } $R\ $R(Literatur) [i|Refactoring >refactoring] Extract Method $R\ $KKKK(Typisierung von Methoden) Eine einheitliche Benennung der Methoden vereinfacht die Lesbarkeit des Codes. Dirk Riehle hat im JavaReport eine [e|Typsierung von Methoden>../Dokumente/mettypes.htm], je nach Verwendungszweck, sowie eine Einteilung nach [e|Methoden Eigenschaften>../Dokumente/metprop.htm] vorgeschlagen. Jeder Methodentyp wird durch ein bestimmtes Prefix im Namen gekenndzeichnet. Hier ein Link zum [e|Original>http://www.riehle.org/papers/2000/jr-2000-method-types.html]. $KKKK(Method Object) $B(Bilder,methobj1.gif,Eine komplizierte Methode) Einige Methoden lassen sich trotz grosser Anstrengungen nicht extrahieren. Dies liegt meist daran, daß sehr viele lokale Variablen verwendet werden, und innerhalb der Methode verschachtelte Kontrollstrukturen und Returns sind. In diesen Fällen hilft es, die Methode durch eine Klasse zu ersetzen. Da in dieser Klasse alle lokalen Variabeln der komplexen Methode als Instanzvariablen umgewandelt sind, lässt sich die Klasse leicht refaktorisieren. # Erzeuge eine neue Klassen, mit dem Name der sich aus der Methode ergibt (Drucken). # Erzeuge für jeden Parameter (printer,kopien) der Methode und für jede lokale Variable (textstil, format) ein Attribut. # Erzeuge ein Attribut für das ursprüngliche Objekt (Dokument). # Erzeuge für jedes Attribut des neuen Objekts die Accessor-Methoden. # Verschiebe den Text der alten Methode in eine neue Methode compute des neuen Objekts. # Ersetze in compute die Parameter und die lokalen Variabeln durch die Accessor-Methoden. # Ersetze in compute die Referenzen auf das Ursprungsobjekt (i.a. this) durch die entsprechende Accessor-Methoden (getDokument). # Gebe der neue Klasse einen Konstruktor mit folgenden Parametern. ** Parameter der alten Methode (printer kopien); ** Parameter für das ursprüngliche Objekt (Dokument) # Ersetze im ursprünglichen Objekt die komplexe Methode durch den Aufruf der neuen Klasse und rufe die Methode compute auf. $B(Bilder,methobj2.gif,Methode in Klasse umgewandelt) $R(Literatur) [i|Refactoring >refactoring] Replace Method with Method Object
[i|Smalltalk Best Practice>bestPatterns] Patterns Method Object $R\