# rule **Repository Path**: shenzlx/rule ## Basic Information - **Project Name**: rule - **Description**: 错误信息处理。 系统复杂到一定程度的时候,出错信息往往需要进行有效的管理。本项目就是在这个方面的一次尝试 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2017-02-26 - **Last Updated**: 2020-12-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 简介 本项目拟对特定规则的业务提供安全的检测机制。 作为核心的一部分,特别对业务中立的静态规则提供了开箱即用的支持。并且对违规的行为采取了抛出异常的处理方式。 # 快速入门 ```java Rule simpleRule = Rule.of("errorCode"); RuleSystem.get().createException(simpleRule); AllwaysRule allwaysRule = Rule.ofAlways("errorCode"); allwaysRule.invalid(); // allwaysRule.invalid("使用时自定义的错误信息,将取代规则中的缺省信息"); boolean condition = true; ConditionalRule conditionalRule = Rule.ofConditional("errorCode") conditionalRule.invalidIf(condition) conditionalRule.invalidIf(condition, "使用时自定义的错误信息,将取代规则中的缺省信息") User user = new User(); PredicateRule notNull = RuleFactory.newPredicateRule("-40", "空指针错误", t -> t != null); notNull.test(user) notNull.test(user, "使用时自定义的错误信息,将取代规则中的缺省信息"); NameSpace.simple("a.b").invalid("errorCode"); ``` # 定义规则 ## 实现`Rule`接口 ```java public enum ShowCaseRule implements Rule { TA("1001", "使用枚举实现Rule接口 1"), ME("1002", "使用枚举实现Rule接口 2"); private String code; private String brief; ShowCaseRule(String code, String brief) { this.code = code; this.brief = brief; } @Override public String code() { return code; } @Override public String brief() { return brief; } } // 必须无参构造函数 public class AnotherRule implements Rule { ... @Override public String code() { return code; } @Override public String brief() { return brief; } } ``` ## 装载规则 ``` String scanPackage = null; ImplementBundle.from(scanPackage).bindTo(RuleSystem.get()); ``` 除了使用`Java`代码实现`Rule`接口的方式定义规则外,还可以使用多种方式定义,已实现的方式有: + 数据库 + 映射 + 属性文件 + TypeSafe Config + YAML 如果想支持更多的方式,需扩展`RuleBundle`接口。 # `Spring` 集成 ```xml ``` # 规则信息选择 以下“同一规则”是指“规则代码相同”的规则。 ## 强制指定 有的时候,规则在某处的信息是不大可能会变,并且是固定的,此时可以强制使用指定的规则信息。此种选择方式具有最高的优先级。 ## 根据优先级 有的时候,多个规则提供者协同工作时,针对同一事实,从各自职责角度有不同的描述,此时必须根据场景选择最合适的描述,否则描述可能会显得生硬。 譬如,对象不能为null,在公共模块只能提供信息:“对象不能不为null”。而在业务用例中出现的时候,往往需要更改为:“用户地址不能为空”等等。 默认的规则系统使用规则容器`PriorityContainer` 提供了这方面的支,优先界别高的将被选择。 ## 根据名称空间 还有的时候,规则信息的选择不是简单的通过优先级可决定的,而是由不同的业务步骤或场景觉得的。如上面所述的,不能为null的规则描述,则一个大的业务用例中,可以精细为“用户地址不能为空”,而在某个更细的业务步骤中,还可以进一步精细为“用户办公地址不能为空”。 为此,项目使用名称空间这一术语对规则系统进行模块化。规则容器`ModularContainer`实现了简单的名称空间处理,当从`ModularContainer`获取一个规则时,遵循以下的规则: + 在当前名称空间内精确匹配,如果匹配成功,则已经获得目标规则; + 否则,如果存在上一级名称空间,移动到上一级的名称空间,重复上一步的逻辑; + 否则,出错,提示目标规则不存在。 使用名称空间时,一般配合NS名称空间辅助类使用。 此时,规则信息是通过名称空间去选择的。 **注意** 名称空间与优先级别是可组合使用。事实上,默认的规则系统就是这样做的。 # 定义业务逻辑规则 尽管不提倡全部的业务规则都通过此种方式提供,但是,对于一些“无状态”的业务规则,的确可以通过PredicateRule提供。 这里的“无状态”是指业务依赖的状态都能通过断言的目标对象提供,而存储在规则中的状态(如果有)整个生命周期中是不变的。 # 模块化规则使用示范 ## 名称空间组织 ```java public enum OMS implements NameSpace { RULE, PAY, SETTLE; } ``` 或者(此种方式,可以有更多的层级): ```java public interface OMS { NameSpace RULE = NameSpace.simeple("OMS"); NameSpace PAY = NameSpace.hierarch("PAY", RULE); NameSpace SETTLE = NameSpace.hierarch("SETTLE", RULE); } ``` ## 名称空间使用 ```java OMS.RULE.invalid("errorCode"); // 等同于 Rule.invalid("errorCode", OMS.RULE); OMS.PAY.invalid("errorCode"); // 等同于 Rule.invalid("errorCode", OMS.PAY); OMS.SETTLE.invalidIf( booleanExpression,"errorCode"); // 等同于 Rule.invalidIf(booleanExpression, "errorCode", OMS.SETTLE); ```