Simple Java DSL with fluent interface

To date, I’ve not put an awful lot of time into considering when it is and isn’t appropriate to introduce a Domain Specific Language into a Java application. It’s something I’ve just started to pay some attention to, having thus far acted on gut feeling that it could be useful in some particular scenario.

Here I’ll present a very small one which I created recently to perform password validation (that is to validate the proposed password meets some minimum strength criteria/standard) in place of an existing static utility method which needed duplicating, with additional requirements, in order to validate a particular flavour of password. I’m taking some time to decide whether it’s actually an appropriate pattern of usage or not.

Here’s the client code making use of it’s fluent interface:

private static final PasswordValidator SECRET_STRENGTH_VALIDATOR = new PasswordValidator()
  .mustContain().atLeast(SECRET_WORD_MIN).and().noMoreThan(SECRET_WORD_MAX).charactersInTotal()
  .mustContain().atLeast(1).numbers()
  .mustContain().atLeast(1).ofTheseCharacters(ALPHA_CHARS)
  .mustContain().atLeast(1).ofTheseCharacters(SPECIAL_CHARACTERS_STRING)
  .mustOnlyContain().anyOfTheseCharacters(ALPHA_CHARS + SPECIAL_CHARACTERS_STRING).or().numbers().andNothingElse()
  .mustBeMixedCase();



Lets for now ignore the fact that this isn’t injected into the dependant client components – there’s only so much refactoring that one can or should attempt in one sitting.

I like this because it reads pretty much like an English sentence (not that ‘English’ or ‘French’, etc, is what the ‘Language’ stands for in DSL, as an old colleague of mine points out here – but in this particular case I think it’s a nice read-ability bonus), rather than a series of methods which must be read through in their entirety, or worse – a big old uber-method which does all of the validation in one hit, before it’s possible to know exactly what the particular validation rules are.

Of course all of that validation code still exists, but now it’s hidden away in validation rules which are constructed according to however the client code makes use of the possible combinations provided by the DSL, and executed when the validate() method is invoked on the validator. In this way many combinations of validation rules can be applied to different validator instances in a self describing way, using minimal extra code to do so.

So lets see where the complexity has been hidden:

public class PasswordValidator {

  private List rules = new ArrayList();

  public boolean validate(String passwordString) {
    char[] password = passwordString == null ? new char[0] : passwordString.toCharArray();
    boolean result = true;
    for(PasswordValidationRule rule : rules) {
      result = result && rule.execute(password);
    }
    return result;
  }

  public MustContainRule mustContain(){
    MustContainRule rule = new MustContainRule(this);
    rules.add(rule);
    return rule;
  }

  public MustOnlyContainRule mustOnlyContain() {
    MustOnlyContainRule rule = new MustOnlyContainRule(this);
    rules.add(rule);
    return rule;
  }

  public PasswordValidator mustBeMixedCase() {
    rules.add(new PasswordValidationRule() {
      @Override
      public boolean execute(char[] password) {
        String passwordString = new String(password);
        String upper = passwordString.toUpperCase(Locale.ENGLISH);
        String lower = passwordString.toLowerCase(Locale.ENGLISH);
        return (passwordString.equals(lower) | passwordString.equals(upper)) == false;
      }
    });
    return this;
  }

}



Ok. So there isn’t anything too complicated here; just a validator with 3 kinds of validation configurable – mixed case, must have something and must ONLY have something. The mixed case requirement is handled here directly as it is a complete requirement in it’s own right. The other two kinds of requirement are meaningless without some further input (what exactly is it that we must have? Must we have a certain number of that thing? etc). You can see that the rules are constructed to a PasswordValidationRule interface (which specifies the execute() method).

Here’s where those extra details are constructed (I won’t show the must ONLY have option – it’s the same but slightly more simple):

public class MustContainRule implements PasswordValidationRule {
  private String chars = null;
  private boolean numbers = false;
  private int from = 0;
  private int to = 0;
  private MustContainRuleDetailAppender detailAppender;

  MustContainRule(PasswordValidator passwordValidator) {
    this.detailAppender = new MustContainRuleDetailAppender(this, passwordValidator);
  }

  public MustContainRuleDetailAppender atLeast(int num) {
    from = num;
    return detailAppender;
  }

  public MustContainRuleDetailAppender noMoreThan(int num) {
    to = num;
    return detailAppender;
  }

  @Override
  public boolean execute(char[] password) {
    boolean isValid =
         validateParticularCharacterRequirements(password)
      && validateOverallLengthRequirements(password)
      && validateNumberRequirements(password);

    return isValid;
  }

  private boolean validateParticularCharacterRequirements(char[] password) {
    if(chars == null) {
      return true;
    }

    int count = 0;
    for (char passLetter : password) {
      count += StringUtils.countMatches(chars, String.valueOf(passLetter));
    }

    return validateCount(count);
  }

  private boolean validateOverallLengthRequirements(char[] password) {
    if(chars != null) {
      return true;
    }
    return  validateCount(password.length);
  }

  private boolean validateNumberRequirements(char[] password) {
    if(numbers == false) {
      return true;
    }
    int count = 0;
    for (char passLetter : password) {
      if(Character.isDigit(passLetter)) {
        count++;
      }
    }
    return validateCount(count);
  }

  private boolean validateCount(int num) {
    boolean pass = from > 0 ? num >= from : true;
    pass = pass && (to > 0 ? num <= to: true);
    return pass;
  }

  public class MustContainRuleDetailAppender {

    private final MustContainRule mustContainRule;
    private final PasswordValidator passwordValidator;

    private MustContainRuleDetailAppender(MustContainRule mustContainRule, PasswordValidator passwordValidator) {
      this.mustContainRule = mustContainRule;
      this.passwordValidator = passwordValidator;
    }

    public MustContainRule and() {
      return mustContainRule;
    }

    public PasswordValidator ofTheseCharacters(String str) {
      mustContainRule.chars = str;
      return passwordValidator;
    }

    public PasswordValidator charactersInTotal() {
      return passwordValidator;
    }

    public PasswordValidator numbers() {
      mustContainRule.numbers = true;
      return passwordValidator;
    }

  }

}

 

And that’s it.
Here we can see that this more complicated rule makes use of a public inner class – this is required in order to limit the client code to make only valid combinations of validation rule detail, such that the client code is only able to construct a single validation rule, in it’s entirety, at a time. This keeps the validation rule construction code simple (and therefore better tested) and the client code is forced to be written in an easy to understand way. This is achieved by controlling which methods are available depending on which object type is returned from each method, and therefore the methods which are then available in the chain; as such being forced to use the api in a specific way is not a bad thing as it actually limits the client code to invoking only a few sensible options at a time. For example, after

  .mustContain().atLeast(SOME_NUMBER)

the client code only has the option of invoking and() in order to add a numerical limit to that same validation rule currently being constructed, ofTheseCharacters() to specify the character set of which SOME_NUMBER must be used, numbers() to specify that SOME_NUMBER of numbers must be present, or finally charactersInTotal() to indicate that the there must be at least SOME_NUMBER of characters total in the password. Each of these methods, apart from and(), returns the original validator object which means that any further validation criteria must exist as another ValidationRule object. Because the MustContain and MustOnlyContain rules are so tightly coupled with their ….DetailAppender classes they have been implemented as inner classes; they’re effectively part of one larger class, but using this technique it’s possible to simulate a kind of context dependant method accessibility (unless the client code decides to deliberately break the mechanism by not chaining their invocations).

DSL_Methods


Why am I concerned about using this pattern rather than simply implementing the rules as private methods in the Utility class, or even as separate rules still, but manually added to a list and executed in sequence? To be honest the more I think about it the better I feel; in this case I feel like I can justify the marginal one off increase in complexity for the reusable simplicity and readability.

The DSL apparatus itself is really pretty simple, and as such it can easily be unit tested with confidence; and with that being the case it feels as though it is actually safer to now implement various validation routines using the DSL’s guiding hand, like this:

private static final PasswordValidator STRENGTH_VALIDATOR_1 = new PasswordValidator()
    .mustContain().atLeast(8).and().noMoreThan(20).charactersInTotal()
    .mustContain().atLeast(2).numbers()
    .mustContain().atLeast(3).ofTheseCharacters(ALPHA_CHARS)
    .mustContain().atLeast(1).ofTheseCharacters(SPECIAL_CHARACTERS)
    .mustOnlyContain().anyOfTheseCharacters(ALPHA_CHARS + SPECIAL_CHARACTERS).or().numbers().andNothingElse()
    .mustBeMixedCase();

private static final PasswordValidator STRENGTH_VALIDATOR_2 = new PasswordValidator()
    .mustContain().atLeast(8).and().noMoreThan(20).charactersInTotal()
    .mustBeMixedCase();

private static final PasswordValidator STRENGTH_VALIDATOR_3 = new PasswordValidator()
    .mustContain().atLeast(10).charactersInTotal()
    .mustContain().noMoreThan(3).numbers()
    .mustOnlyContain().anyOfTheseCharacters(ALPHA_CHARS).or().numbers().andNothingElse();



…than it would be to code them up in the typical, and less structured way. It makes it easier to compare the difference between various validation routines, avoid bugs or assumed behaviour which doesn’t actually exist (which I discovered in the old code during this process, although decent tests should have highlighted that in the first place), and helps to keep that utility class closer to 1 thousand lines than 2 thousand o.O

I’d be interested to hear any criticisms or pointers. Since doing this I’ve been able to cut/paste the code which builds up the validator into emails to send to other non/less-technical people that have needed some information regarding what password-type validation is performed in various places, and it’s worked pretty well. Although it’s a bit of a leap to label this code an internal DSL, it has been easy to share with those less technical people (and is extra convenient for the techies too) because like something such as SQL it has an enforced structure, uses a limited syntax, and is relatively expressive in a self-describing way.

I’m going to go on a hunt now for other scenarios where this kind of mini DSL approach also adds value from an internal perspective, and I’ll add to this if I come up with anything compelling.

Image | This entry was posted in coding, java, software and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s