1

currently I have got this class which implements the Builder pattern, for sake of readibility I have chosen to omit some methods, more precisely I only show the build methods of username.

package dao.constraint;

import java.util.Arrays;

public class AccountConstraint {
    private Constraint<Range<Integer>> accountIdConstraint;
    private Constraint<String> usernameConstraint;
    private Constraint<String> passwordConstraint;
    private Constraint<String> emailConstraint;

    private AccountConstraint(Builder builder) {
        this.accountIdConstraint = builder.accountIdConstraint;
        this.usernameConstraint = builder.usernameConstraint;
        this.passwordConstraint = builder.passwordConstraint;
        this.emailConstraint = builder.emailConstraint;
    }

    public Constraint<Range<Integer>> getAccountIdConstraint() {
        return accountIdConstraint;
    }

    public Constraint<String> getUsernameConstraint() {
        return usernameConstraint;
    }

    public Constraint<String> getPasswordConstraint() {
        return passwordConstraint;
    }

    public Constraint<String> getEmailConstraint() {
        return emailConstraint;
    }

    public Constraint[] getConstraints() {
        return Arrays.asList(this.getAccountIdConstraint(), this.getUsernameConstraint(), this.getPasswordConstraint(), this.getEmailConstraint()).toArray(new Constraint[4]);
    }

    public static class Builder {
        private Constraint<Range<Integer>> accountIdConstraint;
        private Constraint<String> usernameConstraint;
        private Constraint<String> passwordConstraint;
        private Constraint<String> emailConstraint;

        public Builder() {
            this.accountIdConstraint = null;
            this.usernameConstraint = null;
            this.passwordConstraint = null;
            this.emailConstraint = null;
        }

        public Builder username(final String username) {
            this.usernameConstraint = new Constraint<>(Operation.IS, true, username, "username");
            return this;
        }

        public Builder notUsername(final String username) {
            this.usernameConstraint = new Constraint<>(Operation.IS, false, username, "username");
            return this;
        }

        public Builder usernameLike(final String username) {
            this.usernameConstraint = new Constraint<>(Operation.LIKE, true, username, "username");
            return this;
        }

        public Builder usernameNotLike(final String username) {
            this.usernameConstraint = new Constraint<>(Operation.LIKE, false, username, "username");
            return this;
        }

        public AccountConstraint build() {
            return new AccountConstraint(this);
        }
    }
}

As you can see there is very subtle difference between AccountConstraint.Builder.username(String s) and AccountConstraint.Builder.notUsername(String s).

I would like to be able to write something like new AccountConstraint.Builder().not(username(s));. However as I know this is not valid Java syntax if username(String s) is not defined in the calling Java class. I neither wish to repeat the whole AccountConstraint.Builder() again to reach the username(String s) part. Any solutions?

Second question: Can AccountConstraint.getConstraints() be improved or written more simple?

Regards.

3 Answers 3

2

you could make not a method of your builder, setting a flag, which then negates the next constraint.

private boolean negate = false;

public Builder not() {
    negate = true;
}

public Builder username(final String username) {
    this.usernameConstraint = new Constraint<>(Operation.IS, !negate, username, "username");
    negate = false;
    return this;
}
Sign up to request clarification or add additional context in comments.

Comments

2

For your second question:

   public Constraint[] getConstraints() {
        return Arrays.asList(this.getAccountIdConstraint(), 
            this.getUsernameConstraint(),
            this.getPasswordConstraint(),
            this.getEmailConstraint())
            .toArray(new Constraint[4]);
    }

can be re-written to :

   public Constraint[] getConstraints() {
        return new Constraint[] {
            this.accountIdConstraint,
            this.usernameConstraint,
            this.passwordConstraint,
            this.emailConstraint
        };
    }

But IMO, returning a List or Set would be better than an array.

1 Comment

The point is that I got another method accepting a varargs of Constraints, ie. Constraint... constraints and I would like to make it accept that. But your suggestion makes it nicer already :)
2

What I find extremely elegant in this situations is to write a utility class with static factory methods like.

public static Constraint userName(...) { ... }

and to import static blabla.Utility.username;

Then you can write almost declarative human-readable queries in java. This is very much as for the hamcrest library for unit testing where you write something like.

Assert.assertThat(blabla, is(equalTo(nullValue()));

In this case Not should implement Constraint and just negates the nested (referenced) constraint like this:

public static Constraint not(Constraint negated) { return new Not(negated); }

this results in code like

PreparedStatement ps = new QueryBuilder()
 .select()
 .from(table("accounts")
 .where(not(username(equalTo("blabla")))
 .compile();

You can add static factories for boolean combinations:

.where(and(
  .not(...),
  .not(or(...))

Defining constraints like this (static factory methods as opposed to adding them to the builder) thus makes them easily composable.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.