This simple idiom continuingly appears in our search/find use cases, and several other ones, which have parts, in which we find some data to act on it, according to some specific condition.
In search/find use cases, users enter some data pattern, select some options, enter date ranges to narrow or broaden the result of those queries. We needed a way to carry those entries from the user to the DAO layer for the preperation of queries. Hence, we declared an ICriterionBag interface, which is actually a marker interface. In its subclasses, we defined properties and their getters/setters mainly, to carry those entries of users from the presentation layer to the DAO layer. It is also possible to specify some other properties for Hibernate queries, such as maximum result count, or match mode for search patterns and so on.
In DAO layer, we had to somehow translate those entries into Hibernate Criteria objects, and then executed them to have query results. Therefore, we declared an interface, called ICriteriaBuilder, in which there is only one method decleration, which takes an open Hibernate Session, and ICriterionBag object as input, and finally returns a Hibernate Criteria object as output. In subclasses, we first created a Hibernate Critera object, and then added Criterion objects into it, according to the specified entries in ICriterionBag object.
The frequency of defining new ICriterionBag, and ICriteriaBuilder subtypes isn’t so much related with the diversity of use cases, but types of returned entities from those queries. Therefore, you may reuse already defined types in several previous use cases. As a result, ome properties in ICriterionBag are becoming only meaningful for some use cases, and some other properties are for others.