In our project, we have implemented the Specification Pattern with boolean operators (see DDD p 274), like so:
public abstract class Rule {
public Rule and(Rule rule) {
return new AndRule(this, rule);
}
public Rule or(Rule rule) {
return new OrRule(this, rule);
}
public Rule not() {
return new NotRule(this);
}
public abstract boolean isSatisfied(T obj);
}
class AndRule extends Rule {
private Rule one;
private Rule two;
AndRule(Rule one, Rule two) {
this.one = one;
this.two = two;
}
public boolean isSatisfied(T obj) {
return one.isSatisfied(obj) && two.isSatisfied(obj);
}
}
class OrRule extends Rule {
private Rule one;
private Rule two;
OrRule(Rule one, Rule two) {
this.one = one;
this.two = two;
}
public boolean isSatisfied(T obj) {
return one.isSatisfied(obj) || two.isSatisfied(obj);
}
}
class NotRule extends Rule {
private Rule rule;
NotRule(Rule obj) {
this.rule = obj;
}
public boolean isSatisfied(T obj) {
return !rule.isSatisfied(obj);
}
}
Which permits a nice expressiveness of the rules using method-chaining, but it doesn't support the standard operator precedence rules of which can
lead to subtle errors.
The following rules are not equivalent:
Rule<Car> isNiceCar = isRed.and(isConvertible).or(isFerrari);
Rule<Car> isNiceCar2 = isFerrari.or(isRed).and(isConvertible);
The rule isNiceCar2 is not satisfied if the car is not a convertible, which can be confusing since if they were booleans isRed && isConvertible || isFerrari would be equivalent to isFerrari || isRed && isConvertible
I realize that they would be equivalent if we rewrote isNiceCar2 to be isFerrari.or(isRed.and(isConvertible)), but both are syntactically correct.
The best solution we can come up with, is to outlaw the method-chaining, and use constructors instead:
OR(isFerrari, AND(isConvertible, isRed))
Does anyone have a better suggestion?