Pragmatic Domain Specific Languages in Java

1 Kommentar

Neal Ford hielt am Dienstag auf der JAX einen Talk über DSLs und was sie uns für Vorteile bringen. Er zeigt, daß Java bei der Unterstützung für DSL Notationen recht schnell an seine Grenzen stößt und wechselte daher für seine Beispiele dann zu Ruby. Aber da wir bei codecentric hauptsächlich uns mit Java beschäftigen, habe ich mir mal angesehen was möglich ist und was man damit eigentlich tun kann. Einige Entwickler haben bereits das neue Builder pattern von Josh Bloch mittels eines fluid Interfaces umgesetzt. Aber dieses Pattern hat ein Problem: das sogenannte „finishing problem“. Weil das Builder pattern mittels verketteter Methoden arbeitet und Werte via Rückgabeparameter übergibt sind zu verschiedenen Zeitpunkten bestimmte Werte noch nicht initialisiert. Neal zeigte ein von jMock inspiriertes Beispiel wie man dieses Problem in Java lösen kann. Dies inspirierte mein folgendes Beispiel, welches so oder so ähnlich auch schon von GWT oder Swing Entwicklern verwendet wird.

Mein Java DSL Beispiel

Session ormPitfalls = new Session() {{
	speaker("Mirko");
	attending("Fabian");
	attending("a lot of other people");
	at("Rheingoldhalle");
	at(new JaxDate() {{
		day2();
		session2();
	}});
}};

Auf den ersten Blick sieht dies nicht besonders ungewöhnlich aus. Aber… Oh.. doppelte geschweifte Klammern?? Der hier verwendete Trick ist eine  anonyme Klasse, welche Session erweitert (genauso bei JaxDate etwas weiter). Nachdem der Standard Konstruktor ausgeführt wurde laufen die sogenannten Initializer Blocks. Dieser Initializier, welcher in geschweiften Klammern geschrieben wird, enthält nun Methodenaufrufe in einer DSL im Kontext Session.

Braucht man das?

Genauer gesagt hat dieses Vorgehen sogar probleme. Jede Instanz hat seine eigene Klassendefinition, welche zur Laufzeit erstellt werden muss, was eine etwas längere Erstellungszeit nach sich zieht. Ausserdem ist der Speicherverbrauch etwas größer und getClass().getName() liefert natürlich einen anderen Namen.

Performance of Anonymous Classes

Und zuletzt bleibt natürlich die große Frage: Wollen wir wirklich, daß Fachabteilungen in Produktion laufenden Code mittels einer DSL schreiben?

Aber…

… es gibt eine gute Verwendung: Testen! Sehr gerne sähen wir mehr Fachabteilungen am Testen beteiligt, aber selbst mit guten Tools gibt es immernoch einen großen Aufwand an Mocks und Dummies welche erstellt werden müssen. Der oben gezeigte Code um Objektinstanzen zu erstellen ist aber sehr verständlich. Tester können die Domänenmethoden verwenden anstelle sich mit der Erzeugung von Datumsobjekten durch die Java Calendar API rumschlagen zu müssen. Ausserdem ist der Code viel lesbarer und besser. Bis jetzt würde die zu testende und die testende Methode jeweils über eigenen Code ein Datum berechnen. Da es aber keinen Test für den Test gibt wird nicht verifiziert, daß die Datumsberechnung ok ist. Im obigen Beispiel ist die Datumsberechnung jedoch nur einmal implementiert.

Kleiner Calendar Exkurs

Da ich gerade beim Calendar bin. Wie berechnent man „morgen mittag“? So?

Calendar today = Calendar.getInstance();
today.setTime(calendar.getTime());
today.add(Calendar.DAY_OF_YEAR, 1);
today.set(Calendar.HOUR_OF_DAY, 12);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0);

Das sieht nicht nur hässlich aus, sondern hat auch teilweise falsche Semantik. Besser wäre:

Calendar calendar = GregorianCalendar.getInstance();
Calendar today = calendar.clone();
today.setTime(calendar.getTime());
Calendar tomorrow = today.clone();
tomorrow.add(Calendar.DAY_OF_YEAR, 1);
Calendar tomorrowNoon = tomorrow.clone();
tomorrowNoon.set(Calendar.HOUR_OF_DAY, 12);
tomorrowNoon.set(Calendar.MINUTE, 0);
tomorrowNoon.set(Calendar.SECOND, 0);
tomorrowNoon.set(Calendar.MILLISECOND, 0);

uhh.. zumindest versucht dieser Code den Zwischenzuständen korrekte Namen zu geben. Wenn dieser Code aber in einer Methode namens tomorrowNoon() in unserem CustomCalendar/Date Objekt wäre, würde die Semantik viel besser sein (die Methode arbeitet dann nur auf „this.„), insbesondere ist der Code vor dem Anwender versteckt.

Fazit

Domain Specific Languages sind sehr interessant; insbesondere in Programmiersprachen welche eine noch freundlichere Notation erlauben. Jedoch ist die Zeit in vielen Projekten noch nicht reif. Für Tests hingegen stellen sie schon jetzt ein probates Mittel dar um Fachabteilungen mit einzubeziehen und Testcode les- und wartbarer zu machen. Generell ist es immer hilfreich Methodennamen sprechend zu wählen und die Funktionalität an der Fachdomäne auszurichten.

Fabian Lange ist Lead Agent Engineer bei Instana und bei der codecentric als Performance Geek bekannt. Er baut leidenschaftlich gerne schnelle Software und hilft anderen dabei, das Gleiche zu tun.
Er kennt die Java Virtual Machine bis in die letzte Ecke und beherrscht verschiedenste Tools, die JVM, den JIT oder den GC zu verstehen.
Er ist beliebter Vortragender auf zahlreichen Konferenzen und wurde unter anderem mit dem JavaOne Rockstar Award ausgezeichnet.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Lukas Eder

    Interessanter Beitrag! Die doppelt geschweiften Klammern fand ich bis jetzt eher störend, da sie im Lesefluss bei einem Code-Review immer wieder Fragezeichen aufwerfen. Tatsächlich ist die anonyme Klasse störend. Nicht nur für den Class Loader! Wegen der impliziten Referenz auf die äussere Klasse können subtile Memory Leaks entstehen.

    Man kann aber interne DSLs in Java auch ganz anders schreiben, basierend auf einer BNF, welche in eine Menge von Java Interfaces übersetzt werden. Diese Technik habe ich hier in meinem Blog beschrieben:

    blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.