В нашем проекте мы реализовали шаблон спецификаций с логическими операторами (см. DDD стр. 274), например:
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);
}
}
, который допускает приятную выразительность правил, используя цепочку методов, но не поддерживает стандартные правила приоритета операторов, которые могут
привести к тонким ошибкам.
Следующие правила не эквивалентны:
Rule<Car> isNiceCar = isRed.and(isConvertible).or(isFerrari);
Rule<Car> isNiceCar2 = isFerrari.or(isRed).and(isConvertible);
Правило isNiceCar2 не выполняется, если автомобиль не является кабриолетом, что может сбивать с толку, поскольку, если бы они были логическими,
isRed && isConvertible || isFerrari
было бы эквивалентно
isFerrari || isRed && isConvertible
Я понимаю, что они были бы эквивалентны, если бы мы переписали isNiceCar2 в isFerrari.or (isRed.and (isConvertible)), но оба синтаксически верны.
Лучшее решение, которое мы можем найти, - запретить цепочку методов и использовать вместо нее конструкторы:
OR(isFerrari, AND(isConvertible, isRed))
У кого-нибудь есть лучшее предложение?