Overview

Pragmatic Domain Specific Languages in Java

No Comments

On Tuesday I attended Neal Fords session on DSLs and what they do for us. He showed that Java language capabilities for making or using DSLs are limited. But as we at codecentric have a lot Java, let’s have a deeper look to what extend it is possible. While some people already adopted the Builder pattern from Josh Bloch using a fluid interface, this has a problem, called the finishing problem. Because the Builder pattern works as chained method calls, some stuff might not be initialized when required. Neal showed an example, which he took from jMock, how to solve this problem in Java. This impressed me, so I came up with my own idea, which in fact is already done by some GWT or Swing developers.

My Java DSL Example

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

At the first glance this doesn’t look strange.. but… Whoa.. double curly braces?? The trick that is applied here, is that an anonymous class extending Session (and later JaxDate as well) is created. After the default constructor has run the initializer block runs. This initializer, written between that extra pair of curly braces, now contains method calls using the domain language of Session.

Is this useful?

Well, lets start that way: It has a problem. Each instance has its own class definition because they are anonymous classes. Which means slightly higher construction times, slightly higher memory usage and a different getClass().getName().

Performance of Anonymous Classes

And I also think that we are not there yet: We don’t want business people to write production code in a Java DSL. Do we?

But…

… there is a great place for this: Testing! We all want to include business related people in testing, however they find it difficult to setup testcases due the amount of object bootstrapping required. But the above shown creation of that Session object is very easy to understand. Testers can use the domain methods, rather than, for example, fiddling around with creating multiple different Date Instances using the Java Calendar API. In fact this approach creates much better code than the old one. Currently the method being tested will calculate a date and the testcase will calculate it as well. But there is no proof that the testcase calculation is correct.
Using the above code will solve this, because date calculation is implemented only once, so it is less error prone and also easier to understand.

Side note on Calendar

While rating at the Calendar; How do you currently set a date to, lets say, noon tomorrow? Like this?

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);

This does not only look ugly, but also has incorrect semantics. Better semantics would be:

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.. well at least it tries to give all states in between a correct name. If this would be all in a method called tomorrowNoon() on our CustomCalendar/Date object this would be much better (it works only on “this.“) and this kind of ugly code is hidden from the user.

Conclusion

DSLs are worth looking at, but for many projects this is not the time already. However a pseudo DSL notation can be a powerful tool for testing and creating less error prone code, but short and speaking one. As the side note shows, creating methods with a domain meaning does help always, so just do it.

Comment

Your email address will not be published. Required fields are marked *