Gezähmte Ente
Neulich habe ich ein kleines Skript geschrieben, daß über einen SAX-Parser Informationen aus einer XML-Datei in eine Objektstruktur lesen sollte, um Code generieren zu können. Dabei habe ich festgestellt, daß bei dem Handler für einen SAX-Parser einige interessante Probleme auftreten können, die sich mit dem richtigen Werkzeug sehr elegant lösen lassen. Einen Aspekt will ich heute behandeln, einen anderen in Kürze an derselben Stelle.
Ein SAX-Parser liest die XML-Datei einmal durch und löst dabei für Start- und Ende-Tags Events aus. Dadurch, daß man die Events behandelt, läßt sich extrem effizient eine XML-Datei parsen.
Ich hatte nun beim Endetag das Problem, daß ich abhängig vom Typ des Tags einmal ein Objekt an eine Liste anhängen mußte oder das andere Mal dieselben Daten einem vorher erzeugten Objekt zur weiteren Verarbeitung übergeben mußte. Der Methodenaufruf war identisch, das zweite Objekt war aber keine Liste. Ich habe die Methode bei diesem Objekt trotzdem wie bei der Liste "append" genannt und brauchte hier keine weitere Unterscheidung zu machen.
Dies konnte ich nur machen, da ich das Skript in einer Sprache schrieb, die "Duck-Typing" verwendet (in diesem Fall Python), also einen Methodenaufruf durchführt, wenn Name und die Anzahl der Parameter übereinstimmen.
Interfaces
Was für Optionen hätte ich aber gehabt, wenn ich Java (oder C#) verwendet hätte?
Ein neues Interface definieren mit der betreffenden Methode
Dies wäre die geeignete Vorgehensweise, wenn ich über den Code beider Klassen verfügen könnte. Dies geht hier aber nicht, da die List-Klasse zu der Bibliothek der Sprache gehört. Außerdem würden am Ende Interfaces für alle denkbaren Verwendungen bei zentralen Klassen definiert werden, was den Code auch nicht lesbarer macht.
Das Interface für Listen in meiner Klasse implementieren
Das habe ich in ähnlichen Fällen auch schon gemacht. Dabei entstehen eine Reihe von zusätzlichen Methoden in meiner Klasse, die nichts tun als eine "nicht implementiert"-Ausnahme zu werfen. Außerdem ist die Klasse definitiv keine Liste, doch die Deklaration sagt jetzt anderes. Kommentare sind hier dringend notwendig.
Neues Interface und Adapter für List-Klasse
Da ich die List-Klasse nicht ändern kann, kann ich nur einen Adapter schreiben, der das Protokoll umsetzt, also nach außen das für diese Verarbeitung definierte Interface anbietet und den Aufruf dann auf das List-Objekt umlenkt. Umständlich, aber die wohl sauberste Lösung.
Structural Typing
Genau zu dieser Zeit fand ich einen Beitrag von Frank Sommers, in dem es um "structural typing" in Scala geht.
Scala erlaubt es, einen Parameter einer Funktion so zu definieren, daß definiert wird, welche Signatur ein diesem Parameter zugewiesenes Objekt unterstützen muß. In diesem Fall würde also an der Stelle, wo das Objekt zugewiesen wird, definiert, daß alle Objekte, die eine Methode "append" anbieten (mit dem geeigneten Parameter) hier akzeptiert werden. Dies ist im Prinzip dasselbe wie beim "Duck-Typing", aber der Compiler kann die Zuweisung überprüfen.
Wenn es mit Vorsicht verwendet wird, ist dies sicher ein Feature, das es erlaubt Programme leichter zu schreiben und wohl auch verständlicher. Wenn aber zwei Klassen ein Protokoll teilen, so ist ein klassischen Interface sicher die bessere Wahl.
Anmerkung: Z.Z. hat die Scala-Implementierung wohl noch ein kleines Problem mit der Threadsicherheit (siehe).
Keine Kommentare:
Kommentar veröffentlichen