Я внедряю правило-движок в Java. Мой движок правил предопределяет список независимых правил и наборов правил. Правило здесь - это просто кусочек логики. И набор правил объединяет эти простые правила в упорядоченный набор.
Я порядочный разработчик Java, но не Гуру. Мой коллега предложил мне два проекта для этой цели. Я не удовлетворен обоими проектами, поэтому этот вопрос.
Пример правила в моем проекте: Предположим, что входы - это местоположения в США, например, Санта-Барбара, Калифорния, США или Огайо, США, которые обычно находятся в определенном формате с полями города, штата и страны. Тогда у меня могут быть следующие правила:
ПРАВИЛО 1: Город не null
ПРАВИЛО 2: Состояние не null
ПРАВИЛО 3: Страна равна США или США
ПРАВИЛО 4: Длина состояния равна 2
Пример RuleSet в моем проекте:
RULESET: Допустимое местоположение Этот набор правил является упорядоченным набором указанных выше правил.
Два шаблона дизайна, которые я выполнил, следующие:
Дизайн 1: Использование Enum с анонимными внутренними классами
Rule.java
public interface Rule {
public Object apply(Object object);
}
NlpRule.java
public enum NlpRule {
CITY_NOT_NULL(new Rule() {
@Override
public Object apply(Object object) {
String location = (String) object;
String city = location.split(",")[0];
if (city != null) {
return true;
}
return false;
}
}),
STATE_NOT_NULL(new Rule() {
@Override
public Object apply(Object object) {
String location = (String) object;
String state = location.split(",")[1];
if (state != null) {
return true;
}
return false;
}
}),
COUNTRY_US(new Rule() {
@Override
public Object apply(Object object) {
String location = (String) object;
String country = location.split(",")[2];
if (country.equals("US") || country.equals("USA")) {
return true;
}
return false;
}
}),
STATE_ABBREVIATED(new Rule() {
@Override
public Object apply(Object object) {
String location = (String) object;
String state = location.split(",")[1];
if (state.length() == 2) {
return true;
}
return false;
}
});
private Rule rule;
NlpRule(Rule rule) {
this.rule = rule;
}
public Object apply(Object object) {
return rule.apply(object);
}
}
RuleSet.java
public class RuleSet {
private List<NlpRule> rules;
public RuleSet() {
rules = new ArrayList<NlpRule>();
}
public RuleSet(List<NlpRule> rules) {
this.rules = rules;
}
public void add(NlpRule rule) {
rules.add(rule);
}
public boolean apply(Object object) throws Exception {
boolean state = false;
for (NlpRule rule : rules) {
state = (boolean) rule.apply(object);
}
return state;
}
}
RuleSets.java
public class RuleSets {
private RuleSets() {
}
public static RuleSet isValidLocation() {
RuleSet ruleSet = new RuleSet();
ruleSet.add(NlpRule.CITY_NOT_NULL);
ruleSet.add(NlpRule.STATE_NOT_NULL);
ruleSet.add(NlpRule.COUNTRY_US);
ruleSet.add(NlpRule.STATE_ABBREVIATED);
return ruleSet;
}
}
Main.java
public class Main {
public static void main(String... args) {
String location = "Santa Barbara,CA,USA";
RuleSet ruleSet = RuleSets.isValidLocation();
try {
boolean isValid = (boolean) ruleSet.apply(location);
System.out.println(isValid);
} catch (Exception e) {
e.getMessage();
}
}
}
Дизайн 2: Использование абстрактного класса
NlpRule.java
public abstract class NlpRule {
public abstract Object apply(Object object);
public final static NlpRule CITY_NOT_NULL = new NlpRule() {
public Object apply(Object object) {
String location = (String) object;
String city = location.split(",")[0];
if (city != null) {
return true;
}
return false;
}
};
public final static NlpRule STATE_NOT_NULL = new NlpRule() {
public Object apply(Object object) {
String location = (String) object;
String city = location.split(",")[0];
if (city != null) {
return true;
}
return false;
}
};
public final static NlpRule COUNTRY_US = new NlpRule() {
public Object apply(Object object) {
String location = (String) object;
String country = location.split(",")[2];
if (country.equals("US") || country.equals("USA")) {
return true;
}
return false;
}
};
public final static NlpRule STATE_ABBREVIATED = new NlpRule() {
public Object apply(Object object) {
String location = (String) object;
String state = location.split(",")[1];
if (state.length() == 2) {
return true;
}
return false;
}
};
}
RuleSet.java
public class RuleSet {
private List<NlpRule> rules;
public RuleSet() {
rules = new ArrayList<NlpRule>();
}
public RuleSet(List<NlpRule> rules) {
this.rules = rules;
}
public void add(NlpRule rule) {
rules.add(rule);
}
public boolean apply(Object object) throws Exception {
boolean state = false;
for (NlpRule rule : rules) {
state = (boolean) rule.apply(object);
}
return state;
}
}
RuleSets.java
import com.hgdata.design.one.NlpRule;
import com.hgdata.design.one.RuleSet;
public class RuleSets {
private RuleSets() {
}
public static RuleSet isValidLocation() {
RuleSet ruleSet = new RuleSet();
ruleSet.add(NlpRule.CITY_NOT_NULL);
ruleSet.add(NlpRule.STATE_NOT_NULL);
ruleSet.add(NlpRule.COUNTRY_US);
ruleSet.add(NlpRule.STATE_ABBREVIATED);
return ruleSet;
}
}
Main.java
public class Main {
public static void main(String... args) {
String location = "Santa Barbara,CA,USA";
RuleSet ruleSet = RuleSets.isValidLocation();
try {
boolean isValid = (boolean) ruleSet.apply(location);
System.out.println(isValid);
} catch (Exception e) {
e.getMessage();
}
}
}
Лучший подход к дизайну/шаблон? Как вы можете видеть, дизайн 2 избавляется от интерфейса и перечисления. Вместо этого он использует абстрактный класс. Мне все еще интересно, есть ли более эффективный дизайн/подход к реализации того же самого.
Активация с использованием блоков инициализатора:
Теперь в случае обоих конструкций выше. Скажем, если мне нужно создать экземпляр внешнего класса, чтобы использовать его в моей логике приложения, то я вынужден использовать блоки инициализаторов, которые я не совсем понимаю, является ли это хорошей практикой. Пример ниже приведен ниже:
Дизайн 1:
...
STATE_ABBREVIATED(new Rule() {
private CustomParser parser;
{
parser = new CustomParser();
}
@Override
public Object apply(Object object) {
String location = (String) object;
location = parser.parse(location);
String state = location.split(",")[1];
if (state.length() == 2) {
return true;
}
return false;
}
});
...
Дизайн 2:
...
public final static NlpRule STATE_ABBREVIATED = new NlpRule() {
private CustomParser parser;
{
parser = new CustomParser();
}
public Object apply(Object object) {
String location = (String) object;
location = parser.parse(location);
String state = location.split(",")[1];
if (state.length() == 2) {
return true;
}
return false;
}
};
...
Эксперты Java, пожалуйста, приложите немного света! Также, пожалуйста, укажите, обнаружите ли какие-либо недостатки в этих двух конструкциях. Мне нужно знать плюсы и минусы, связанные с каждым из проектов, чтобы помочь мне принять правильное решение. Я рассматриваю лямбда, предикаты и несколько других шаблонов, как это было предложено некоторыми пользователями в комментариях.