在Java中设计规则引擎的有效设计模式/风格是什么?

Sha*_*kar 20 java enums design-patterns class anonymous-class

我正在用Java实现规则引擎.我的规则引擎预定义了独立规则和规则集的列表.这里的规则只是一个逻辑.规则集将这些简单规则组合成有序集.

我是一个体面的java开发人员,但不是大师.我的同事为此建议了两个设计.我对这两个设计都不满意,因此这个问题.

我的项目中的规则示例: 假设输入是美国的位置,例如,美国加利福尼亚州圣巴巴拉或美国俄亥俄州,通常采用一些定义明确的城市,州和国家/地区字段.然后我可以有如下规则:

规则1:城市不为空
规则2:状态不为空
规则3:国家等于美国或美国
规则4:州长等于2

我的项目中的RuleSet示例:

RULESET:有效位置此规则集是上面定义的规则的有序集合.

我实现的两个设计模板如下:

设计1:将Enum与匿名内部类一起使用

Rule.java

public interface Rule {
    public Object apply(Object object);
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

设计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;
        }

    };

}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更好的设计方法/模式? 如您所见,设计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;
        }

    });
...
Run Code Online (Sandbox Code Playgroud)

设计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;
        }

    };
...
Run Code Online (Sandbox Code Playgroud)

请高手指教Java专家!如果您发现上述两种设计存在任何缺陷,请指出.我需要了解与每个设计相关的利弊,以帮助我做出正确的决定.我正在研究一些用户在评论中提出的lambdas,谓词和其他几种模式.

spr*_*ter 6

这是一个有趣的问题,有许多可能的答案.在某种程度上,解决方案将取决于个人偏好.我经常遇到类似的问题并提出以下建议.请注意,这些对我有用,但可能不适合您的需要.

  1. 使用enum.从长远来看,我觉得它们private static在错误检查和有效使用它们的有用容器(EnumSet等)方面比成员有很多优势.

  2. 在抽象类上使用接口.在Java 8之前,使用抽象类是有用的.对于default会员来说,现在没有充分的理由(只是我的意见 - 我相信其他人会不同意).枚举可以实现接口.

  3. 在Java 8中,与每个"规则"相关联的逻辑可以嵌入到lambda表达式中,这使得枚举的初始化代码更加清晰.

  4. 保持lambda非常短 - 最多只有一两个命令(最好是一个没有块的表达式).这意味着将任何复杂的逻辑拆分为单独的方法.

  5. 使用单独的枚举来对您的规则进行分类.没有充分的理由将它们全部放在一起并通过拆分它们可以使构造函数变得简单,具有与其域相关的lambda表达式.请参阅下面的示例以了解我的意思.

  6. 如果您具有规则的层次结构,请使用复合设计模式.它灵活而强大.

所以把这些建议放在一起我会建议像:

interface LocationRule{
    boolean isValid(Location location);
}

enum ValidValueRule implements LocationRule {
    STATE_NOT_NULL(location -> location.getState() != null),
    CITY_NOT_NULL(location -> location.getCity() != null);

    private final Predicate<Location> locationPredicate;
    ValidValueRule(Predicate<Location> locationPredicate) {
        this.locationPredicate = locationPredicate;
    }

    public boolean isValid(Location location) {
        return locationPredicate.test(location);
    }
}

enum StateSizeRule implements LocationRule {
    IS_BIG_STATE(size -> size > 1000000),
    IS_SMALL_STATE(size -> size < 1000);

    private final Predicate<Integer> sizePredicate;
    StateSize(Predicate<Integer> sizePredicate) {
        this.sizePredicate = sizePredicate;
    }
    public boolean isValid(Location location) {
        return sizePredicate.test(location.getState().getSize());
    }
}

class AllPassRule implements LocationRule {
    private final List<LocationRule > rules = new ArrayList<>();
    public void addRule(LocationRule rule) {
        rules.add(rule);
    }
    public boolean isValid(Location location) {
        return rules.stream().allMatch(rule -> rule.isValid(location));
    }
}

class AnyPassRule implements LocationRule {
    private final List<LocationRule > rules = new ArrayList<>();
    public void addRule(LocationRule rule) {
        rules.add(rule);
    }
    public boolean isValid(Location location) {
        return rules.stream().anyMatch(rule -> rule.isValid(location));
    }
}

class NegateRule implements LocationRule {
    private final Rule rule;
    public NegateRule(Rule rule) {
        this.rule = rule;
    }
    public boolean isValid(Location location) {
        return !rule.isValid(location);
    }
}
Run Code Online (Sandbox Code Playgroud)

这些可以用作:

AnyPassRule anyPass = new AnyPassRule();
anyPass.addRule(ValidValueRule.STATE_NOT_NULL);
anyPass.addRule(new NegateRule(StateSize.IS_SMALL_STATE));
anyPass.isValid(location);
Run Code Online (Sandbox Code Playgroud)

等等.


edd*_*uld 4

已经有很多(开源)Java 规则引擎 - 请查看http://java-source.net/open-source/rule-engineshttp://drools.org/

您可以从使用/检查其中之一的源代码开始(注意它不满足您的要求的地方),然后从那里开始。