📜 ⬆️ ⬇️

An example of using fluent interface in java to describe domain objects

Disclaimer: I found the article in my drafts. I wrote a year and a half or two ago, why I did not publish it - I do not remember. I looked, it seems, not completely useless, let it be in the public domain.

Recently, the use of domain specific languages ​​(DSL) - languages ​​“sharpened” for a specific subject area has become quite relevant. The word “language” in this context does not necessarily imply a new programming language, it is often possible to manage with the good old ones.

On Habré not a single article about the fluent interface in the context of java, so I would like to share my experience with the application.
')
Thanks to Alepar ( alepar ) for the tip-off on an article by Fowler (a big DSL fan)

The idea of ​​a fluent interface is that the API is a subset of the domain-based description language. And this happiness is available from the basic programming language.

Fowler's text can be read by reference, but I will give my example.

Suppose we need to programmatically construct an object corresponding to this xml:
<Results> <unit>123</unit> <unit>321</unit> <ResultSet> <ResultsType> <ResultType>ABC</ResultType> <resAllTime>4.000000000000000</resAllTime> <resAllTime>5.000000000000000</resAllTime> <ResultsTimeBucket> <bucketDate>777</bucketDate> <value>3.000000000000000</value> <value>0.000000000000000</value> </ResultsTimeBucket> <ResultsTimeBucket> <bucketDate>888</bucketDate> <value>1.000000000000000</value> <value>5.000000000000000</value> </ResultsTimeBucket> </ResultsType> </ResultSet> </Results> 


The meaning of what is written is that unit 123 has a total value of 4, which is made up of 3 on date 777 and another 1 on date 888. And unit 321 has a total value of 5, which entirely fell on date 888.

The API that is used to build such an object is a plugin over pluses (JNI):

 Results r = new Results(); r.CallocResults(new int[] {123, 321}, new int[] {777, 888}); r.SetValue(4.0, "ABC", 0); // 0 - unit index in this case corresponds to "123" r.SetValue(5.0, "ABC", 1); r.SetBucketedValue(3.0, "ABC", 0, 0); r.SetBucketedValue(1.0, "ABC", 1, 0); // 1 - bucketDate index (corresponds to 888), 0 - unit index r.SetBucketedValue(5.0, "ABC", 1, 1); 


In general, everything in the case, there is nothing to complain about, but to read and imagine what will be the result is practically unreal ...
Initially, it was necessary for the unit test, so using a code with an unobvious result is not a very good idea, since the visibility of the test unit and its documenting component is lost.

I thought hard and tried to portray the fluent interface for the same. Its implementation was surprisingly non-trivial (due to the peculiarities of the underlying JNI interface), but it looks very nice:

 Results r = new ResultsBuilder() .withUnits(123, 321) .result("ABC") .value(123, 4.) .value(321, 5.) .on(777) .value(123, 3.) .on(888) .value(123, 1.) .value(321, 5.) .build(); 


In general, having mastered several techniques for building DSL in Java, the task becomes technical: the eyes are afraid, the hands are doing.

Source: https://habr.com/ru/post/83372/


All Articles