Refactoring und der Aberglaube..


Nein, hier geht es nicht darum, Refactoring als Aberglaube abzutun und alle Welt zur Nutzung silberner Pflöcke oder Knoblauch gegen jeden Nutzer von Refactorings aufzurufen.

Stattdessen geht es darum, ein paar Fetzen Fehlglaube zu identifizieren und anzuprangern, die sich um das Thema Refactoring und Typsicherheuit ranken.

Anlass dazu lieferte mir folgendes Zitat aus einem Blogpost von Travis Griggs , der auf seine Eindrücke von einem Refactoring-Workshop auf der OOPSLA eingeht:

One of the things that struck me in this workshop as well as some other presentations, is how valuable having a simple syntax is. The guys working on the Eclipse refactorings for Java have an object model of the AST that has 100+ node types. Having been working on the RB formatter lately, with its AST object model which has 11 concrete node types, I can see how fortunate I am. When you have 100+ node types, whatever you’re doing with it, whether it is refactoring or pretty-printing, the number of combinations becomes a geometric complexity. That was born out in some of the side discussions. Apparently, while many of the basic refactorings that we have in the Refactoring Browser are enjoyed by the Eclipse/Java folks, there’s a long list of bugs of various edge cases where they don’t work. This is sad and unfortunate, but it’s not surprising when your AST is that complex.

Nun ist es ja nicht wirklich ein Geheimnis, dass Java über die Jahre einige wichtige Features dazugelernt hat, und dabei die Sprache immer komplexer wurde. Generics etwa machen das Leben des Entwicklers sehr viel einfacher, wenn es um die Vermeidung von “zu viel” Typecasterei geht. Foreach ist zwar noch weit von einem do: in Smalltalk entfernt, aber es ist ein Schritt in die richtige Richtung.
Erkauft ist dieser Komfort mit Komplexität. Komplexität kann man handhaben, und glücklicherweise gibt es Tools wie Eclipse, die es dem Entwickler erleichtern, mit dieser Komplexität umzugehen. Eclipse und seine kleinen Helferlein erleichtern die Entwicklungsarbeit ungemeint, und ein Leben ohne solch mächtige Tools ini Java wäre sehr viel härter.
Travis’ Zitat zeigt aber auch die Kehrseite der Medaille auf: Komplexität zu handhaben, ist komplex und Fehleranfällig.
Erst vor einigen Wochen hatte ich in einer Präsentation beim Kunden in einem Smalltalk-Projektteam den Fehler gemacht, von einer wohl nur 97-prozentigen Verlässlichkeit zu sprechen, wenn es um die Zuverlässigkeit von Refactorings und den Ausschluss der Möglichkeit geht, dass ein Refactoring Nebeneffekte hat. Damit war der Vortrag zunächst mal erledigt: einer der Teilnehmer (externer Dienstleister) im Workshop ist ein harter Verfechter der Theorie, dass gerade für größere Projekte nur die Typsicherheit von Java ein Garant für den Erfolg eines Projektes sei. Wir verbrachten die nächste halbe Stunde mit der Diskussion, inwieweit denn Refactorings sinnvoll seien, wenn sie keine Refactorings seien, wo sie doch per Definition keine Nebeneffekte haben und die vollständige Gleichartigkeit der Ergebnisse der Ausführung refactorisierter Codestücke garantieren.
(Glücklicherweise hatten wir einen Moderator, der uns nach einer Weile wieder zum Thema zurückführte, das eigentlich gar keinen Java-Smalltalk-Endkampf beinhalten sollte…)
Nun scheint es mir aber so, dass die oben zitierte Passage darauf hin deutet, dass meine Schätzung von 97 % zwar vielleicht nicht vertrauenswürdig genug erscheint, um sich auf Refactoring vollständig zu verlassen, aber ganz offensichtlich ist es mit der Garantie und Freiheit von Nebenwirkungen in den Eclipse JDT (=Java development Tools) auch nicht allzu weit her.

Ich habe bisher in meiner Entwicklungsarbeit noch keinen Fall erlebt, in dem ein Refactoring in VA Smalltalk oder VisualWorks den Code verändert hätte, ohne vorher zu warnen.

Ich gebe zu, dass es in einer dynamisch getypten Sprache sehr schwer möglich ist, bei gleichlautenden Methodennamen zu unterscheiden, welche man umbenennen darf und welche nicht. Deswegen fragt der RB (=Refactoring Browser) auch immer, wenn er mehrere Implementors einer Methode findet.  In der Theorie ist das in Java (oder C# oder C++) sehr viel einfacher: der RB weiss ja zu jeder Zeit, welche Klasse er da anfasst, und wo im Quelltext eine Methode genau dieser Klasse gerufen werden soll. Ein “Rename Method” ist im statischen Universum einfacher und sicherer zu bewerkstelligen.

Aber sind wir in Java wirklich in einem statischen Universum unterwegs? Sehr viele Features, die moderne Entwicklung in Java erleichtern, basieren auf der Entkopplung von Typen. Ein Beispiel, das mir aktuell  sehr häufig begegnet: JFace Data Binding. Hier wird zum gegenseitigen Update zwischen Modellobjekten und GUI-Elementen auf PropertyChangeListener zurückgegriffen. Deren Hauptaufgabe besteht darin, Änderungen an einer Variable über einen Publish/Subscribe-Mechanismus zu propagieren. Und siehe da: es wird typfrei über Reflection gearbeitet:

setName(String newName) {
this.name=newName;
this.changeSupport.firePropertyChange("name", null, null);
}

So findet sich selbst in der Dokumentation von Eclipse und weiteren Tutorials die Hinweise darauf, dass Data Binding sich mit den aktuellen Refactoring tools nicht refaktorisieren lässt. Schliesslich gibt es keinen für den Compiler erkennbaren Link zwischen der Variable name mit einem eindeutigen Typ String und dem Property Namen “name”.

Klar lässt sich hier über Code-Generatoren, Namenskonventionen und Annotations viel machen, und es spricht nichts dagegen, hier einen Standard zu definieren, der es möglich macht, verlässliche Refactorings für diese Fälle zu schreiben.

Einziger Nachteil: Die funktionieren solange, wie sich der Entwickler an die Konventionen halten.  Und die Komplexität der Refactorings hat eine Facette mehr.

Ich denke, man sollte sich langsam von einigen Sagen und Mähren lösen, die die Typsicherheit und deren Vorzüge in Java gegenüber dynamisch getypten Sprachen lösen:

  • Java-Programme sind heutzutage deutlich weniger typsicher, als man gemeinhin denkt. Viele moderne Frameworks setzen viel Energie in die Umgehung von Typsicherheit
  • Java ist inzwischen ein sehr komplexes Gebilde, und es ist sehr schwer, wirklich gute Entwicklerzu finden, die mit dieser Komplexität verantwortungsvoll und ausgewogen umgehen können
  • Die Komplexität ist teilweise aufzufangen durch den Einsatz von Codegeneratoren. GUI-Builder wie SWT Designer, die viel Tipparbeit für EventListener etc. sparen, machen einen sehr guten Job. Allerdings bleibt in Projekten mit längerer Laufzeit nicht aus, dass diesergenerierte Code nachher auch mal wieder angepasst werden muss.Im Idealfall kann hier einfach der alte Code weggeworfen und durch neuen, toolgenerierten Code ersetzt werden. In der Realität sieht es leider meist anders aus.
  • Refactoring ist auch in statisch getypten Sprachen nicht hundertprozentig sicher. Es bleibt immer die theoretische Chance auf offene Flanken. Genau wie in dynamische getypten Sprachen
  • Refactoring sollte grundsätzlich flankiert sein von Unit Tests, egal wieviele hundert Prozent Sicherheit in Refactoring hineindefiniert wurden.

One thought on “Refactoring und der Aberglaube..

  1. Sehr interessanter Artikel!

    Zeigt er noch nebenbei, dass die Typenlosigkeit eine wichtige und gute Eigenschaft einer Programmiersprache ist. Denn die reale Welt (Und jedes Programm bildet immer Teile der realen Welt nach) ist auch typenlos.

    In Java wird die Typenlosigkeit von hinten reingebastelt. In Smalltalk ist sie von Anfang an „reindesignt“.

    Ich kenne Java nur oberflächlich, wie unterscheidet sich Foreach in Java vom Smalltalk do:?

    Grüße Thomas

Comments are closed.