In diesem Artikel führe ich eine Nachbetrachtung zum IList Dojo durch.
Nachdem ich meine fünfteilige Artikelreihe (Links zu den einzelnen Teile am Ende dieses Artikels) beendet hatte, habe ich die Lösung von Stefan Lieser angeschaut. Seine darin beschriebenen Erfahrungen habe ich mit meinen verglichen und seine Umsetzung meiner Umsetzung gegenübergestellt. Dabei geht es mir nicht hauptsächlich darum, ob meine Lösung richtig oder falsch bzw. besser oder schlechter ist. Ich möchte vielmehr betrachten, ob es andere Herangehensweisen und andere Lösungsansätze gibt und weitere Schlüsse aus dem Dojo ziehen können.
Dojo-Start
Der Solution-Aufbau beinhaltet besteht bei beiden aus einem Implementations- und einem Test-Projekt. Auch Stefan Lieser hat sich dafür entschieden, die Test-first-Vorgehensweise anzuwenden. Er verwendet aber nicht Visual T# sondern C# um die Tests zu implementieren. Zudem hat er noch NCrunch eingesetzt, ein Tool das die Tests automatisch im Hintergrund ausführt. Das Resultat wird dabei direkt in Visual Studio im Source Code Editor auf der jeweiligen Zeile angezeigt. Sicher ein Tool, das sich lohnt einmal genauer anzuschauen.
Die erste Methode
Bei den ersten Methoden sind wir auch ähnlich vorgegangen. Wir haben uns beide entschieden, mit Tests zu Count und Add() zu beginnen, um erste Resultate zu haben. Auch den internen Datencontainer haben wir in diesem Schritt eingeführt.
Hier trennen sich unsere Wege
Als nächstes hat sich Stefan Lieser an den Enumerator heran gewagt, den ich erst in Teil 4 implementiert hatte. Ich hatte mich als nächstes auf den Indexer konzentriert, welcher dafür von Stefan Lieser erst zum Schluss implementiert wurde.
Dementsprechend sind dann auch in den nächsten Schritten unterschiedliche Methoden angepackt worden. Stefan Lieser hat auch noch Themen angedacht, die ich nicht bedacht hatte, wie z.B. Performance-Betrachtungen.
Kreuzverhör
Doch wie sieht das Resultat aus?
Machen wir den Kreuztest: Meine Tests führen die Bibliothek von Stefan Lieser aus und seine Tests benutzen meine Library. Im Idealfall würden beide Testsuiten keine Fehler melden, alle Tests wären grün und wir könnten zufrieden Feierabend machen.
Aber wie so oft entspricht die Realität nicht dem Idealfall. In diesem Fall heisst das, dass meine Library in Stefan Liesers Testsuite 6 Fehler verursacht (von insgesamt 31 Tests) und seine Bibliothek in meiner Testumgebung 11 Fehler provoziert (bei einem Total von 39 Tests).
Doch was sind die Gründe für diese Fehler. Haben wir Fehlerfälle vergessen, interpretieren wir die Vorgaben des Interfaces IList<T> anders oder gibt es andere Gründe?
Untersuchung 1: Meine Bibliothek
Meine Library verursacht zwei fehlschlagende Tests, da ich beim Aufruf von RemoveAt() mit einem Index ausserhalb der erlaubten Werte eine ArgumentOutOfRangeException werfe. Die Testfälle erwarten aber eine IndexOutOfRangeException. Laut der Dokumentation von Microsoft soll aber die ArgumentOutOfRangeException geworfen werden, auch wenn die IndexOutOfRangeException wohl passender wäre.
Ein Fehler tritt auf, da ich diesen Test vergessen hatte: Löschen eines Elementes aus der Liste mittels Remove() wenn die Liste noch leer ist. Dieser Aufruf warf bei mir bei der Suche in IndexOf() wegen einer vergessenen Überprüfung eine Exception, statt wie erwartet false zurückzugeben. Dieser Fehler hatte auch zur Folge, dass zwei weitere Tests fehlschlugen, die ebenfalls eine leere Liste verwendeten.
Der letzte Test schlägt fehl, da Stefan Lieser die Methode CopyTo() anders interpretiert als ich. Der zweite Parameter dieser Funktion gibt laut Dokumentation von Microsoft an, ab welcher Position im Array die Daten eingefüllt werden sollen („The zero-based index in array at which copying begins.“). Stefan Lieser hat diesen Parameter so interpretiert, dass dies der Startindex in der LinkedList ist und die Werte vorher ignoriert werden sollen. Hier eine Grafik, die den Unterschied darstellen soll:
Ein kurzer Test des Verhaltens von List<T>’s CopyTo() zeigt das von mir beschriebene Verhalten, dementsprechend entspricht aus meiner Sicht das Verhalten von Stefan Liesers Implementation nicht den Anforderungen des Interfaces. Aber die Dokumentation von IList<T> dürfte in diesem Bereich noch klarer sein, besonders da der Parametername Interpretationen zulässt. Bei der Dokumentation von List<T>’s CopyTo() hat es ein Beispiel, welches das Verhalten ebenfalls aufzeigt.
Untersuchung 2: Stefan Liesers Bibliothek
Bei meinen Tests, losgelassen auf Stefan Liesers Bibliothek, kommen die beiden Diskrepanzen auch wieder zum Vorschein. Die unterschiedlichen Exceptions sind für zwei fehlgeschlagene Tests verantwortlich. Ebenfalls für zwei fehlgeschlagene Tests ist die unterschiedliche Interpretation bei der CopyTo()-Methode verantwortlich.
Die restlichen 7 Tests schlagen Aufgrund fehlender Überprüfung der Parameter fehl. Dies betrifft den Indexer, die Insert()- und die CopyTo()-Methode, wobei bei der letzten ein Test von mir auch nicht optimal ist, da ich Nichts (eine leere Liste) Nirgends (null als array) hin kopieren will.
Fazit
Diese Nachbetrachtung fand ich lehr- und aufschlussreich. Zum einen habe ich in meinem Code noch Fehler entdeckt und dadurch gesehen, dass mir trotz Test-first-Vorgehensweise mögliche Fehlerfälle durch die Lappen gingen. Zum andern wurde mir wieder einmal aufgezeigt, dass die Schnittstellenbeschreibungen unterschiedlich aufgefasst werden können und am besten mit einer öffentlich zugänglichen Beispielimplementation gefestigt werden. Dies trifft natürlich entsprechend auch für APIs und Protokolle zu.
Trotz identischem Start haben sich die Wege getrennt und es sind teilweise unterschiedliche Resultate herausgekommen. Grosse Unterschiede bei der Herangehensweise oder bei den Lösungsansätzen sind mir nicht aufgefallen. Bei den Details gab es aber Unterschiede, die im Einsatz der Bibliothek dann auch zu Problemen führen könnten.
Artikelreihe „LinkedList Dojo mit Visual T#“