views:

29

answers:

2

Where should I locate the code for validating an employee ID (badge) that will be entered in multiple html forms through out my application?

Currently I have it in the STKUserForm.java which is used when people login (authenticateUser) or request their password (requestPassword). This works great so far. I have to send my many thanks to BalusC. A lot of what I have working so far is based on his DAO/Servlets blog. Thanks BalusC!!!!!!!!!!

But now, I am creating another use case besides logging in, where a supervisor assigns a task (CommitmentItemForm.java) to an employee by entering the employee's badge. I'd rather not duplicate my business logic (someday a valid badge may have 7 digits)there so I feel I need to move it out of STKUserForm.

STKUserForm.java - called from the login page (by a servlet) and both methods mentioned above call the processBadge which then calls the validateBadge method.

public final class STKUserForm extends Form {
    public STKUser authenticateUser(STKUser LoginUser) {
        <snip> 
            processBadge(LoginUser.getBadge());
        <snip>
        return authenticatedUser;
    }
    public void requestPassword(STKUser loginUser) {
        <snip> 
            processBadge(LoginUser.getBadge());
        <snip>
    }

public void processBadge(String badge) throws DAOException {
    try {
        validateBadge(badge);
    } catch (ValidatorException e) {
        setError(FIELD_USERBADGE, e.getMessage());
    }
}

public void validateBadge(String badge) throws ValidatorException, DAOException {
    if (badge != null) {
        if (!FormUtil.isBadge(badge)) {
            throw new ValidatorException("Please enter valid badge (6 digits, numbers only, and no 'E').");
        } else if (!STKUserDAO.isValidEmployee(badge)) {
            throw new ValidatorException("This is not a valid badge of any EB Employee.");
        }
    }
}
}

So where should I move the validateBadge method?? STKUser bean?? FormUtil??? Some other utility class??? I'm unsure because it makes a call to STKUserDAO. I'll want to validate an employee badge for many use cases through out this and other applications.

A: 

I would have the STKUserDAO.isValidEmployee() provide all the functionality of the STKUserForm.validateBadge() method, throw the exceptions inside STKUserDAO.isValidEmployee() instead. Then your validation is available where ever you are accessing the DB.

BruteForce
perhaps, but the FormUtil.isBadge(badge) is called to check that it is a well formed badge before I make a hit to the database. I'd rather not make a call to the database if they accidentally type an 'E' in front of the number (some legacy apps here required E + badge number)
jeff
Just putting the code into the STKUserDAO doesn't mean you need to make a call to the db. You can have the FormUtil.isBadge(badge) call before the initiation of the db connection and if it fails exit the method with a the exception and no db call is made.
BruteForce
I did some refactoring and your suggestion is working pretty good so far.
jeff
A: 

One solution is to make the bade id a domain value object:

public final class BadgeId {
   private final String value;
   public BadgeId(String value) {
      if(value not valid badge id) throw IllegalArgumentException("invalid: " + value);
      this.value = value;
   }
   public String getValue() {
      return value;
   }
   // equals and hashcode
}

Now you can use BadgeId instead of String in all of your methods. The Action form can have a getBadgeId() : BadgeId method for convenience. Since BadgeId is immutable and the constructor enforces validity, you don't ever have to worry about invalid BadgeId objects flying around.

If this sounds crazy, think about JDK classes such as Integer, Float, etc. They didn't have to exist. We could just pass String objects around and call Float.validate(String) and Float.isValid(String) etc. Instead, domain objects for building programming (Float, Double, etc.) were created.

Another useful method for the BadgeId class might be:

public static BadgeId toBadgeId(String value) { ... }

If you expect to have many types of BadgeId objects, all with different validation rules, you could use something like:

public abstract class BadgeId {
    ... same constructs / methods as before except ...

    protected abstract boolean isValid(String value);
}

public final class NumericBadgeId extends BadgeId {
    public NumericBadgeId(String value) {
        super(value);
    }
    protected boolean isValid(String value) {
        return true if value contains all numbers; false otherwise
    }
}
LES2
Thanks, let me digest your answer for a bit. It seems like such a change to what I have, but then again I am new to this. I was hoping for a cut and paste of my validateBadge method to a more appropriate Class/location
jeff