首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Java中设计规则引擎的有效设计模式/样式是什么?

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

Stack Overflow用户
提问于 2015-05-25 03:29:00
回答 5查看 5.6K关注 0票数 23

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

我是一个不错的java开发人员,但不是Guru。为此,我的同事向我推荐了两种设计方案。我对这两种设计都不满意,因此提出了这个问题。

项目中一个规则的例子:说输入是美国的位置,例如,圣巴巴拉,CA,美国或OH,美国通常是在城市,州和乡村字段中定义的某种格式。然后我可以有以下几条规则:

规则1:城市null

规则2:状态不为空

规则3:国家等于美国或美国

规则4:状态长度等于2

我的项目中的RuleSet示例:

规则集:有效位置该规则集是上述定义规则的有序集合。

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

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

Rule.java

代码语言:javascript
复制
public interface Rule {
    public Object apply(Object object);
}

NlpRule.java

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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:

代码语言:javascript
复制
...
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:

代码语言:javascript
复制
...
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;
        }

    };
...

爪哇专家请投一些光!另外,如果您发现上述两种设计中有任何缺陷,请指出。我需要知道每一种设计的利弊,以帮助我做出正确的决定。我正在研究lambdas、谓词和一些用户在评论中建议的其他模式。

EN

回答 5

Stack Overflow用户

发布于 2015-05-25 04:17:10

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

  1. 使用enum。从长远来看,我觉得他们在错误检查和有用的容器(EnumSet等)方面比EnumSet成员有很多优势。能有效地利用它们。
  2. 对抽象类使用接口。在Java 8之前,有一些有用的理由可以使用抽象类。对于default成员,现在没有很好的理由(只是我的观点--我相信其他人会不同意)。枚举可以实现接口。
  3. 在Java 8中,与每个“规则”相关联的逻辑可以嵌入到lambda表达式中,从而使枚举的初始化代码更加清晰。
  4. 保持lambdas非常短-最多只有一两个命令(最好是一个没有块的表达式)。这意味着将任何复杂的逻辑分解成一个单独的方法。
  5. 使用单独的枚举对规则进行分类。没有很好的理由将它们放在一起,通过将它们分开,您可以通过拥有与其域相关的lambda表达式来简化构造函数。请看下面的例子,看看我的意思。
  6. 如果您有规则的层次结构,请使用组合设计模式。它又灵活又健壮。

因此,把这些建议放在一起,我建议如下:

代码语言:javascript
复制
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);
    }
}

因此,例如,要实现这样一条规则,即位置必须位于城市或不小的州:

代码语言:javascript
复制
AnyPassRule cityOrNonSmallState = new AnyPassRule();
cityOrNonSmallState.addRule(ValidValueRule.CITY_NOT_NULL);
cityOrNonSmallState.addRule(new NegateRule(StateSize.IS_SMALL_STATE));
return cityOrNonSmallState.isValid(location);
票数 8
EN

Stack Overflow用户

发布于 2015-05-25 03:41:43

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

您可以从使用/检查其中的一个源开始(注意它不符合您的要求),然后从那里开始。

票数 4
EN

Stack Overflow用户

发布于 2015-05-25 05:45:33

与静态字段的接口:

代码语言:javascript
复制
public interface NlpRule 
{
    Object apply(Object object);

    NlpRule CITY_NOT_NULL = object ->
    {
        String location = (String) object;
        String city = location.split(",")[0];
        return ...true/false;
    };

    // etc. 

有些人可能更喜欢方法而不是函数对象。

代码语言:javascript
复制
public interface NlpRule 
{
    Object apply(Object object);

    static boolean cityNotNull(Object object) // java8: static method in interface
    {
        String location = (String) object;
        String city = location.split(",")[0];
        return ...true/false;
    };

    // etc. 

}

// use method reference as functional object

NlpRule rule = NlpRule::cityNotNull;

ruleset.add( NlpRule::cityNotNull );

或者您可以同时拥有方法和字段。

代码语言:javascript
复制
public interface NlpRule 
{
    Object apply(Object object);

    NlpRule CITY_NOT_NULL = NlpRule::cityNotNull;
    static boolean cityNotNull(Object object)
    {
        ...
    };

示例规则都是String->boolean,不确定为什么NlpRule是Object->Object。如果规则确实可以接受/返回不同的类型,那么您可能应该泛化NlpRule<T,R>

CustomParser可以存储在包私有助手类中。

代码语言:javascript
复制
class NlpRuleHelper
{
    static final CustomParser parser = new CustomParser();
}

--

public interface NlpRule
...
    NlpRule STATE_ABBREVIATED = object -> 
    {
         ...
         location = NlpRuleHelper.parser.parse(location);
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30430818

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档