Hallo Spaß-Coder.
Heute geht es wieder um das Thema Rafactoring und eine weitere Möglichkeit, Abhängigkeiten aufzulösen, um unseren Code unter Testkontrolle zu bringen. Wem ist schon einmal eine Klasse über den Weg gelaufen, welche im Konstruktor mehrstufige Abhängigkeiten erzeugt, um später eine korrekt initialisierte Klasse verwenden zu können? Hier gibt es sicherlich verschiedene Ansätze, was man hätte besser machen können. Wer einen Blick auf das „D“ von SOLID werfen möchte, kann dies im Artikel Dependency Inversion Principle machen. Jetzt ist aber das Kind bereits in den Brunnen gefallen und der Konstruktor sieht so aus wie oben beschrieben.
Mit der Idee, nur mit Tests abgesicherten Code umzubauen, wird es schwierig die Klasse sinnvoll zu testen, wenn alle Abhängigkeiten im Konstruktor selbst erzeugt werden. Hier haben wir ohne Weiteres keine Einflussmöglichkeiten. Wie an vielen anderen Stellen auch kann ein Mocking-Framework dabei unterstützen, Abhängigkeiten zwischen Klassen aufzuheben. Doch geht es auch ohne Framework?
Extract and Override Factory Method
Das nachfolgende Muster hilft uns sehr schnell dabei, Herr über die oben geschilderte Ausgangslage zu werden. Das Prinzip ist einfach: wenn wir das Erzeugen der Abhängigkeiten beeinflussen wollen, brauchen wir nur den zugehörigen Code in eine separate Methode zu schieben (Extract Method) und in einer abgeleiteten Klasse zu überschreiben. Diese neue Klasse ist dann das subject under test, welches sich natürlich genauso verhält wie die ursprüngliche Klasse mit Ausnahme der Erzeugung der Abhängigkeiten.
Die extrahierte Methode wird Fabrik-Methode genannt. Unser Produktions-Code bleibt unverändert erhalten und „fabriziert“ die benötigten Klassen wie es bisher der Konstruktor getan hat. In der Testklasse können wir jedoch die Fabrik-Methode durch Überschreibung in der Form anpassen, dass die Abhängigkeiten auf von uns bereitgestellte Fake- oder Mock-Objekte geändert werden. Damit haben wir die Abhängigkeit für unseren Test aufgehoben und können die Klasse unter Testkontrolle bringen.
In der abgeleiteten Klasse können die Fake- oder Mock-Objekte sogar als Instanzvariablen zugewiesen werden (ggf. über einen weiteren Konstruktor). So wird die Klasse zusätzlich noch sehr flexibel für verschiedene Testszenarien.
Hier noch mal alle erforderlichen Schritte.
Anleitung
- Objekterstellung im Konstruktor identifizieren.
- Extrahieren der gesamten Logik für die Erstellung in eine Fabrik-Methode (protected).
- Abgeleitete Klasse für den Test erstellen und Fabrik-Methode überschreiben.
Schlechter Stil – aber testbar!
Mit Hilfe dieses Vorgehens erreichen wir unser Ziel. Wir schaffen die Möglichkeit, dass der Code testbar wird. Er wird aber auch zunächst schlechter was den Programmierstil angeht. Statische Code-Analyse-Tools wie etwa PMD werden dieses Pattern als schlechtes Code-Design anmeckern. Prinzipiell birgt das Aufrufen überschriebener Methoden im Konstruktor einer Klasse Gefahren.
Beim Refactoring sollen wir aber ohnehin besonders vorsichtig sein. Ist unser Code einmal testbar, können wir auch die Erstellung der Klasseninstanz sauber nach dem SOLID-Prinzip aufräumen um auch unserem Anspruch an Code-Design-Regeln zu entsprechen.
Zusammenfassung
Wie bei allen vorgestellten Refactoring-Patterns steht bei diesen Vorgehensweisen im Vordergrund, dass der Einfluss auf den Produktionscode so gering wie möglich ist. Zu leicht passiert es, dass das Verhalten des Codes ungewollt geändert wird und damit unbewusst Fehler eingebaut werden. Die Fabrik-Methode macht nicht nur den Produktions-Code lesbarer, sondern bietet uns in sehr wenigen Schritten die Möglichkeit, Abhängigkeiten aufzulösen und die gewünschten Tests vor dem Refactoring zu erstellen.
Wir wünschen euch viel Spaß beim Ausprobieren.
Eure Spaß-Coder
Dieser Artikel basiert neben unseren Erfahrungen auf folgenden Quellen:
- Feathers, Michael C. Effektives Arbeiten mit Legacy Code: Refactoring und Testen bestehender Software. mitp Verlags GmbH & Co. KG, 2011.
- PMD