mercredi 21 février 2018

JQuery-like single / multi container class in Java

I'm looking to implement some sort of jQuery-like object in which, acting as container, can contain one or more objects.

In Jquery you can e.g do: $('.bla').addClass('ble');. It is unknown whether this is applied on one or more objects, yet the method is only one.

I was also very surprised to see that in java-8 you can do CompletableFuture.allOf(CompletableFuture<?>... cfs).join().

No matter how many objects you pass, you will get a single CompletableFuture<Void> and you can call any instance method that will act on all internal objects.

Still, its implementation is definitely too complex for my case.

My base interface looks like this

public interface Response< T extends DataTransferObject< ? > > {

    // instance methods

    public boolean isSuccess();
    public String getMessage();

    public default boolean isUndefined() {
        return this.equals(ActionResponse.UNDEF);
    }

    @SuppressWarnings("unchecked")
    public default Response< T > mergeWith(final Response< ? super T > response) {
        return Response.allOf(this, response);
    }

    public default boolean isMultiResponse() {
        return this instanceof ActionResponse.ActionMultiResponse;
    }
    /* -------------------------------------------------------------------------------------------------------------- */

    // factory methods

    @SuppressWarnings("unchecked")
    public static < U extends DataTransferObject< ? > > Response< U > allOf(final Response< ? super U >... responses) {

        Objects.requireNonNull(responses);

        if (responses.length == 0) return ( Response< U > ) ActionResponse.UNDEF;

        return new ActionResponse.ActionMultiResponse< U >(responses);
    }

    public static < U extends DataTransferObject< ? > > Response< U > from(final boolean success, final String message) {
        return new ActionResponse<>(success, message);
    }
}

The package-protected class looks like this (incomplete):

/* Package-protected implementation for the Response< T > type */
class ActionResponse< T extends DataTransferObject< ? > > implements Response< T > {

    static final Response< ? extends DataTransferObject< ? > > UNDEF = new ActionResponse<>();

    private final boolean success;
    private final String message;

    private ActionResponse() {
        this.success = false;
        this.message = null;
    }

    ActionResponse(final boolean success, final String message) {
        this.success = success;
        this.message = message;
    }

    // getters

    static final class ActionMultiResponse< T extends DataTransferObject< ? > > extends ActionResponse< T > {

        private final String[] messages;

        ActionMultiResponse(final boolean success, final String message) {
            super(success, message);
            this.messages = null;
        }

        ActionMultiResponse(final Response< ? super T >... responses) {

            /* must check if any of the following is already a MultiActionResponse */
            // TODO

            this.messages = new String[responses.length];

            for (int i = 0; i < responses.length; i++) {
                this.messages[i] = responses[i].getMessage();
            }
        }

        @Override
        public String getMessage() {

            if (this.messages == null) return super.getMessage();

            return Arrays.toString(this.messages); // dummy string merger
        }
    }
}

It's important that classes outside of this package can access only the interface as I want the implementation details to be hidden. Nobody should care on whether a given instance is a single or a multi.

The only way you can build a Response< T > would be via the factory methods provided or the instance method Response::mergeWith. E.g:

Response< SpotDTO > response = Response.from(true, "message");


If was wondering if this is indeed a pattern and if such pattern has a name? If so, what guidelines should I follow?

What is a better way to implement this concept?

For instance, I do not like that the ActionResponse holds a String type for message whereas ActionMultiResponse has String[].

Still I do not want to have only one object with a String array and place if needed only one object inside.

Aucun commentaire:

Enregistrer un commentaire