Auf der JAOO hatte ich das Glück ein Tutorial zum  Thema domänenspezifische Sprachen (Domain Spezific Language - DSL) besuchen zu können. Interessant war es, die verschiedenen Elemente des Themas einmal in einen Zusammenhang gebracht zu bekommen. Und wie immer, wenn Martin Fowler etwas darstellt, bekommt man ein paar neue Begriffe beigebracht, die es erlauben das bisher Gesehene und Gefühlte besser zu benennen und in einen systematischen Zusammenhang zu stellen.

Fluent Interfaces sind eine Form einer DSL, genauer einer internen DSL, genauer: sie sind ein syntaktisches Mittel eine interne DSL zu realisieren. Und Fluent Interfaces sind angesagt. Ich habe gesehen wie in einigen Projekten mithilfe eines Fluent Interfaces ein Bereich modelliert wurde. Mein Arbeitsfeld ist der eCommerce und so sind es oftmals Regeln, die den Checkout-(Bezahl-)Prozess steuern, die so modelliert werden. Ich kenne diese Form des Interfaces aber auch aus Konvertern, die eine Form von Daten in eine andere übersetzen. Bisher, vor dem Tutorial, hatte ich ein ungutes Gefühl, wenn ich die Beschreibung der Regeln in einem Fluent Interface sah und die Implementierung der Regeln mit der Interpretation der internen DSL verbunden war. Seitdem, seit dem Tutorial, kann ich das etwas genauer beschreiben. DSLs seien, so Fowler, dafür da ein Semantisches Modell zu instantiieren. Das Sematische Modell modelliere den Gegenstandsbereich. Es sei kein Domänenmodell, weil auch Bereiche, die nicht zur Domäne gehörten, mit einer DSL modelliert werden könnten. (Insofern sei aber jedes Domänenmodell ein Semantisches Modell.) Nehmen wir das Beispiel des Regelwerks für einen Checkoutprozess. Ich brauche ein Sematisches Modell, das meine Regeln, meine Lieferzeiten, meine Bezahlarten modelliert. Dieses Modell ist als solches testbar. Um jetzt für einen konkreten Shop ein spezifisches Regelwerk zu instantiieren, benutze ich eine DSL. Die kann ich ebenfalls separat testen. Der Test besteht darin, zu kontrollieren ob eine bestimmte Beschreibung eine bestimmte Form des Regelwerkes instantiiert. Der Test ist vollständig entkoppelt vom Test der Funktion des Regelwerks. Das entscheidende in diesem Zusammenhang ist das Sematische Modell. Es ist substantiell, dass hier der Bereich, der abgebildet werden soll, möglichst explizit modelliert ist. Ich kenne Implementierungen, die die DSL nutzen um eine sehr allgemeine Datenstruktur zu befüllen, bspw. einen Hash, und diese dann in entsprechend imperativen Code nutzen. Hier ist die DSL eher kontraproduktiv, weil sie verdeckt, dass die Domäne nicht wirklich modelliert wurde. Durch die DSL entsteht der Eindruck, es gäbe die Konstrukte, die die Domäne abbilden.

Der Sinn einer DSL lässt sich, Fowler folgend, auch so beschreiben: Anstatt das Command/Query-API des Sematischen Modells zu benutzen, wird eine Fluent Interface eingesetzt, um das Modell zu popularisieren.Das Semantische Modell stellt ein alternatives Berechnungsmodell dar - bspw. ein Regelwerk. Wird das Command/Query-API dieses Modells benutzt, ist das alternative Programm - implizit in der Instantiierung des konkreten Regelwerks enthalten - unsichtbar. Die DSL erlaubt es das alternative Programm als solches sichtbar zu machen.

(Fussnote: Wer mit einer internen DSL programmiert hat oftmals das Gefühl einem deklarativeren Programmierstil zu folgen. Das liegt nicht daran, dass DSLs per se einem deklarativeren Programmierstil folgen, sondern daran dass ein Semantisches Modell instantiiert wird, welches ein regelorientiertes Paradigma ausdrückt.)

Ein ganz kleines Beispiel: Mocha, die Ruby-Mock-Library, mit einem fiktiven Command/Query-API. Hier wird ein Mock programmiert.

# Command/Query-API
object = mock()
expectation = object.create_expectation()
expectation.set_method_name(:expected_method)
expectation.set_parameters([:p1, :p2])
expectation.set_return_value(:result)

Das Gleiche als interne DSL (mit Original-Mocha-Syntax)

# Fluent Interface
object = mock()
object.expects(:expected_method).with(:p1, :p2).returns(:result)

Im zweiten Fall ist die Programmierung des Mocks deutlich zu erkennen. Seine Programmierung nähert sich dem programmierten Methodenaufruf an. Im ersten Fall muss ich mir den zu erwartenden Methodenaufruf beim Lesen im Kopf zusammensetzen.

(Fussnote: Der Begriff Command/Query-API meint ein sinnvolles Prinzip für die Konstruktuion von Objekten. Eine Methode sollte entweder eine Query sein, das bedeutet, sie liefert einen Wert zurück und ändert den Zustand des Objektes nicht, oder ein Command, was bedeutet, dass die Methode eine Zustandveränderung des Objektes bewirkt, aber keinen Wert zurückliefert (Javasprech: void). Ein Fluent Interface gegen die Unterscheidung, weil es in der Regel den Zustand eines Objektes verändert und das Objekt zurückliefert, damit solche Methodenketten wie oben möglich werden.)