Markus KARG wrote:
>
> WebDAV uses rather complex XML for its entities, and currently one
> must write a lot of nested constructors to create valid bodies:
>
>
>
> return new MultiStatus(new Result(x), new Result(y);
>
>
>
> As fluent APIs are trendy due to its much improved readbility, and
> since JAX-RS itself comes with ResponseBuilders having fluent APIs, I
> want to add something like a fluent "WebDavBuilder" which makes
> creation of responses easier.
>
The usage of the builder pattern (a.k.a fluent APIs) was definitely
attractive to me, as well, when I first approached JAX-RS and Jersey.
>
>
>
> Unfortunately I am not much used to fluent APIs and recognized that
> designing a *good* fluent API is not so easy.
>
>
>
> For example, how to add multiple instances of the same class to a root
> element?
>
>
>
> return WebDavBuilder.multiStatus().result(x).result(y); ?
>
>
>
> return WebDavBuilder.multiStatus(new Result(x), new Result(y)); ?
>
Technically, either of these can work. My actual recommendation,
though, is to support both approaches (with a bit of a twist). As an
example of this, I can point you to jersey-multipart where I used this
kind of pattern to add a BodyPart to a MultiPart that contains it. A
couple of different options are provided (these signatures are part of
MultiPart):
// Add a BodyPart you have already constructed and return "this"
MultiPart
public MultiPart bodyPart(BodyPart bodyPart) {
getBodyParts().add(bodyPart);
return this;
}
// Create a new BodyPart and add it, then return "this" MultiPart
public MultiPart bodyPart(Object entity, MediaType mediaType) {
BodyPart bodyPart = newBodyPart(entity, mediaType);
return bodyPart(bodyPart);
}
The first option lets me construct a new BodyPart separately, tweaking
it however is needed, and add the finished product. The second option
gives me essentially a convenience mechanism to create the new BodyPart,
but without having to explicitly do so in my code.
One important principle illustrated in my example above (and sometimes
violated in the JAX-RS and Jersey instances of the pattern) is that the
"this" object you are building up should not change as the expression
gets evaluated. In your first pattern, I'm assuming that the result(x)
and result(y) methods return the MultiStatus instance you are adding to,
so that would be obeying the pattern. However, what if result() was
itself a builder that let you construct a Result? That would be
confusing to the user, because the "." connectors in the expression
after this would refer to that Result, and you could never get back to
adding things to the MultiStatus.
So, I'd recommend thinking about something like this on MultiStatus:
// Construct a Result with this status code, add it to the
MultiStatus, and return this MultiStatus
MultiStatus result(int code);
// Construct a Result with this status code and message,
// add it to the MultiStatus, and return this MultiStatus
MultiStatus result(int code, String message);
// Add the specified Result to the MultiStatus, and return this
MultiStatus
MultiStatus result(Result result);
If there are other options on how to create a Result (best place to
figure that out would be check the constructors on Result for available
argument patterns), you could add similar signatures here.
Craig
>
>
> ...?
>
>
>
> Since a lot of people are participating to this forum having much more
> experience with fluent APIs than I have, I want to ask all of those
> gurus: What is the best way to design that? Currently both of my ideas
> seem to be not any better that the original
> nested-constructors-approach... :-(
>
>
>
> Thanks!
>
> Markus
>