В последнее время стало достаточно актуальным использование domain
specific languages (DSL) — языков «заточенных» под конкретную предметную
область. Слово «язык» в данном контексте не обязательно подразумевает
именно новый язык программирования, зачастую можно обойтись и старым
добрым.
Идея fluent interface в том, что API представляет собой некоторое
подмножество домен-ориентированного языка описания. Причем это счастье
доступно из базового языка программирования.
Текст Фаулера можно почитать по ссылке, я же приведу свой пример.
Допустим, нам нужно программно сконструировать объект соответствующий вот этому 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>
Смысл написанного в том, что у юнита 123 общее значение 4, которое
складывается из 3 на дату 777 и еще 1 на дату 888. А у юнита 321 общее
значение 5, которое целиком пришлось на дату 888.
API которое используется для построения такого объекта является надстройкой над плюсами (JNI):
Results r = new Results();
r.CallocResults(new int[] {123, 321}, new int[] {777, 888});
r.SetValue(4.0, "ABC", 0);
r.SetValue(5.0, "ABC", 1);
r.SetBucketedValue(3.0, "ABC", 0, 0);
r.SetBucketedValue(1.0, "ABC", 1, 0);
r.SetBucketedValue(5.0, "ABC", 1, 1);
В общем все по делу, придраться не к чему, но читать и представлять себе что будет в результате практически нереально …
Изначально это нужно было для юнит теста, поэтому использовать код с
неочевидным результатом — не очень удачная идея, т.к. теряется
наглядность юнит теста и его документирующая составляющая.
Я крепко подумал и попытался изобразить fluent interface для того же
самого. Его реализация оказалась на удивление нетривиальной (из-за
особенностей underlying JNI интерфейса), но зато выглядит он очень
симпатично:
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();
В общем-то, освоив несколько приемов построения DSL на Java, задача становится технической: глаза боятся, руки делают.
|