Motor de reglas: el diseño abstracto de la lógica de reglas en forma de "1 y (2 | 3)"

Abstracción de expresiones lógicas y condiciones bajo reglas.

Para cualquier regla, incluidas múltiples condiciones, se puede resumir en el siguiente formato json:

Entre ellos, 1, 2 y 3 representan respectivamente 3 condiciones; la forma en que se implementa esta regla es "1 y (2 | 3)", por supuesto, puede ser de forma similar. La siguiente definición json

{
    
    
    "logic": "1 & (2 | 3)",
    "conditions":[
        {
    
    
            "isNot":true,
            "itemId":1,
            "left":{
    
    
                "value":"1",
                "isVar":true
            },
            "operator":"<",
            "right":{
    
    
                "value":"3",
                "isVar":true
            }
        },
        {
    
    
            "isNot":false,
            "itemId":2,
            "left":{
    
    
                "value":"1",
                "isVar":true
            },
            "operator":"==",
            "right":{
    
    
                "value":"3",
                "isVar":true
            }
        },
        {
    
    
            "isNot":false,
            "itemId":3,
            "left":{
    
    
                "value":"number",
                "isVar":true
            },
            "operator":"startsWith",
            "right":{
    
    
                "value":"666",
                "isVar":true
            }
        }
    ]
}

La abstracción de cada condición incluye el ID correspondiente , que se utiliza para asociar expresiones lógicas (es decir, 1, 2, 3), y los tres elementos de la condición: variable izquierda , variable derecha , operador .

{
    
    
   "isNot":false,
   "itemId":1,
     "left":{
    
    
         "value":"1",
         "isVar":true
     },
     "operator":"<",
     "right":{
    
    
         "value":"3",
         "isVar":true
     }
}

Codificación y abstracción de la lógica expresiva.

El procesamiento de expresiones lógicas 1 & (2 | 3)sigue siendo similar al de las expresiones de sufijo: 2 | 3 ejecuta el resultado de & con 1. 1 & (2 | 3)Después del análisis es el siguiente:

inserte la descripción de la imagen aquí

Expresión del operador: contiene datos de operación y operadores , definidos de la siguiente manera

public class OperatorExpr implements Expr {
    
    
    // 操作数列表
	private List<Expr> operands;
    // 操作符的名称
	private String operator;

	/**
	 * !标志,not在表达式和操作符中都可能出现,用这个key来区分这两种情况,主要用于统计分析
	 *
	 * 其余取值无意义
	 */
	private String notToken;

	/**
	 * 表达式的标识,不是所有的Operator都有
	 */
	private int id = -1;

	public OperatorExpr(String operator, List<Expr> operands) {
    
    
		this(operator, operands, "");
    }

	public OperatorExpr(String operator, List<Expr> operands, String notToken) {
    
    
		this.operator = operator;
		this.operands = operands;
		this.notToken = notToken;
	}

	public List<Expr> getOperands() {
    
    
		return operands;
	}

	public void setOperands(List<Expr> operands) {
    
    
		this.operands = operands;
	}

	public String getOperator() {
    
    
		return operator;
	}

	public void setOperator(String operator) {
    
    
		this.operator = operator;
	}

	public int getId() {
    
    
		return id;
	}

	public void setId(int id) {
    
    
		this.id = id;
	}

	@Override
	public void accept(ExprVisitor visitor) {
    
    
		visitor.visit(this);
	}

}

regla

condiciones bajo las reglas

public class Condition {
    
    
    /**
     * 序号。
     *
     * 一般从 1 开始。
     */
    private int itemId;

    /**
     * 操作符。
     *
     * 比如 : 大于,不等于 等
     */
    private String operator;

    /**
     *
     */
    private boolean isNot;

    /**
     * 左变量
     */
    private Operand left;

    /**
     * 右变量
     */
    private Operand right;


abstracción del operador

inserte la descripción de la imagen aquí

public abstract class AbstractBaseOperator extends AbstractIdentifiableOperator{
    
    

    /**
     * @param context 引擎执行上下文
     * @return 识别结果
     */
    @Override
    public EvalResult doEval(EngineExecutionContext context) {
    
    
        Variable op1 = operands.get(0);
        Variable op2 = operands.get(1);
        if (op1 == null || op2 == null) {
    
    
            throw new IllegalArgumentException("argument in  operator can't  be null.");
        }


        Object a, b = null;
        a = op1.eval(context);

        //op2.eval()是个耗时操作。如果op1是Unknown,op2.eval就没必要做了。
        if (a == EvalResult.Unknown) {
    
    
            return EvalResult.Unknown;
        }

        if (op2 != null) {
    
    
            b = op2.eval(context);
        }
        if (b == EvalResult.Unknown) {
    
    
            return EvalResult.Unknown;
        }

        return invokeOperator(a, b, context);
    }


    private EvalResult invokeOperator(Object a, Object b, EngineExecutionContext context) {
    
    
        try {
    
    
            // 第一遍求值时,忽略高耗时操作符
            return EvalResult.valueOf(apply(a, b, context));
        } catch (Throwable e) {
    
    
            throw e;
        } finally {
    
    

        }
    }


    /**
     * 操作符具体逻辑
     *
     * @param a       左操作实参
     * @param b       右操作实参
     * @param context 上下文
     * @return true/false
     */
    protected abstract Boolean apply(Object a, Object b, EngineExecutionContext context);

    @Override
    public void accept(EvaluableVisitor visitor) {
    
    
        visitor.visit(this);
    }

}
  1. Se requiere la lógica de tomar y evaluar los operandos izquierdo y derecho, el método eval
  2. Realizar operaciones de operador, aceptar método.

La razón por la que se utiliza la interfaz es principalmente para implementar operaciones retrasadas o que requieren mucho tiempo, es decir, ejecutar cuando es necesario, consulte la revisión: https://doctording.blog.csdn.net/article /detalles/121593411

definir clase de regla

public class Rule implements Serializable, Evaluable<RuleResult>{
    
    
    protected String id;
    protected String title;
    private RuleEntry ruleEntry;
    protected volatile Evaluable<EvalResult> expression;

    public Rule() {
    
    
    }

    public Rule(RuleEntry entry) {
    
    
        this.id = entry.getId();
        this.expression = parse(entry);
        this.ruleEntry = entry;
    }

    @Override
    public RuleResult eval(EngineExecutionContext context) {
    
    

        long startTime = System.nanoTime();
        RuleResult result = new RuleResult(EvalResult.False, this);
        try {
    
    
            EvalResult evalResult = getExpression().eval(context);
            result.setEvalResult(evalResult);
        } catch (Exception ab) {
    
    
            result = new RuleResult(ab);
        } finally {
    
    
            result.setCost(System.nanoTime() - startTime);
        }

        return result;
    }

    @Override
    public void accept(EvaluableVisitor visitor) {
    
    
        visitor.visit(this);
    }

    //
    public Evaluable<EvalResult> getExpression() {
    
    
        return expression;
    }

    // parse表达式并执行
    public Evaluable<EvalResult> parse(RuleEntry rawRule) {
    
    
        try {
    
    
            Expr expr = parseExpr(rawRule.getExpression());
            return toEvaluable(rawRule, expr);
        } catch (Exception e) {
    
    
            throw new IllegalArgumentException("parse rule error, ruleId: " + rawRule.getId(), e);
        }
    }

    public Expr parseExpr(Expression rawExpr) {
    
    
        if (rawExpr == null) {
    
    
            throw new IllegalArgumentException("absence of raw expression");
        }

        return parseExpr(rawExpr.getLogic(), rawExpr.getConditions());
    }

    public Expr parseExpr(String logic, List<Condition> conditions) {
    
    
        ExprParser parser = new ExprParser(logic, conditions);
        return parser.expr();
    }

    public static Evaluable<EvalResult> toEvaluable(RuleEntry rawRule, Expr expr) {
    
    
        if (expr == null) {
    
    
            return null;
        }
        String ruleId = rawRule.getId();
        EvalExprVisitor visitor = new EvalExprVisitor();
        expr.accept(visitor);

        return visitor.getEvalExpr();
    }
}
  • Las propiedades de las reglas contienen expresiones condicionales y su lógica de operación.
  • Cuando se inicializa la regla, la expresión condicional se inicializa y verifica.
  • La ejecución de reglas es 1 & (2 | 3)el proceso de ejecutar expresiones lógicas.

aplicación de reglas

public class RuleTest {
    
    
    public static void main(String[] args) {
    
    
        String logic = "1 &(2|3)";
        List<Condition > conditions = new ArrayList<>();
        Condition condition1 = new Condition();
        condition1.setItemId(1);
        Operand operandLeft = new Operand();
        operandLeft.setIsVar(true);
        operandLeft.setValue("age");
        operandLeft.setModifier("age");
        condition1.setLeft(operandLeft);
        condition1.setOperator(Operator.GT);
        Operand operandRight = new Operand();
        operandRight.setIsVar(false);
        operandRight.setValue("18");
        operandRight.setType("int");
        condition1.setRight(operandRight);
        conditions.add(condition1);

        Condition condition2 = new Condition();
        condition2.setItemId(2);
        Operand operandLeft2 = new Operand();
        operandLeft2.setIsVar(false);
        operandLeft2.setValue("2");
        condition2.setLeft(operandLeft2);
        condition2.setOperator(Operator.LT);
        Operand operandRight2 = new Operand();
        operandRight2.setIsVar(false);
        operandRight2.setValue("1");
        condition2.setRight(operandRight2);
        conditions.add(condition2);

        Condition condition3 = new Condition();
        condition3.setItemId(3);
        Operand operandLeft3 = new Operand();
        operandLeft3.setIsVar(true);
        operandLeft3.setValue("number");
        operandLeft3.setModifier("number");
        condition3.setLeft(operandLeft3);
        condition3.setOperator(Operator.CONTAINS_STRING);
        Operand operandRight3 = new Operand();
        operandRight3.setIsVar(false);
        operandRight3.setValue("666");
        condition3.setRight(operandRight3);
        conditions.add(condition3);

        Expression expression = new Expression(logic, conditions);

        RuleEntry ruleEntry = new RuleEntry();
        ruleEntry.setId("1");
        ruleEntry.setExpression(expression);
		// 构造规则
        Rule rule = new Rule(ruleEntry);
		// 构造规则执行的上下文
        EngineExecutionContext engineExecutionContext = new EngineExecutionContext();
        Map<String, Object> ctx = new HashMap<>();
        ctx.put("age", 19);
        ctx.put("number", "666abc");
        engineExecutionContext.setData(ctx);
		// 执行规则并打印结果
        RuleResult ruleResult = rule.eval(engineExecutionContext);
        System.out.println(ruleResult);
    }
}

Recorrido de expresiones para la suma del cálculo del operador

public class EvalExprVisitor implements ExprVisitor {
    
    

	private Operator<?, EvalResult> expr = null;
	private Operator<Evaluable<?>, ?> currentParent = null;

	@Override
	public boolean visit(IdentifierExpr x) {
    
    
		if (null != currentParent && null != x) {
    
    
			currentParent.addOperand(new Identifier(x.getConditionId(),x.getName()));
		}
		return false;
	}

	@Override
	public boolean visit(LiteralExpr x) {
    
    
		if (null != currentParent && null != x) {
    
    
			currentParent.addOperand(new Literal(x.getValue()));
		}
		return false;
	}

	@SuppressWarnings({
    
     "unchecked", "rawtypes" })
	@Override
	public boolean visit(OperatorExpr x) {
    
    
		String operator = x.getOperator();

		// 	获取操作符实际例子
		Operator opexpr = OperatorLoader.getOperator(operator);

		if (expr == null) {
    
    
			expr = opexpr;
		}else {
    
    
			currentParent.addOperand(opexpr);
		}

		Operator<Evaluable<?>, ?> oldParent = currentParent;

		currentParent = opexpr;
		List<Expr> operands = x.getOperands();
		if (operands != null && !operands.isEmpty()) {
    
    
			for (Expr expr : operands) {
    
    
				expr.accept(this);
			}
		}
		currentParent = oldParent;
		return false;
	}

	public Evaluable<EvalResult> getEvalExpr() {
    
    
		return expr;
	}
}

Diferentes tipos de expresión: la variable izquierda, el operador y la variable derecha se abstraen en una expresión y luego se analizan y ejecutan durante el proceso de ejecución específico.

public interface ExprVisitor {
    
    

    /**
     * 访问变量表达式
     *
     * @param identifierExpr
     * @return
     */
    boolean visit(IdentifierExpr identifierExpr);

    /**
     * 访问常量表达式
     *
     * @param literalExpr
     * @return
     */
    boolean visit(LiteralExpr literalExpr);

    /**
     * 访问操作符表达式
     *
     * @param operatorExpr
     * @return
     */
    boolean visit(OperatorExpr operatorExpr);

}

ilustrar:

  • Las expresiones se pueden almacenar en caché antes de iniciar el programa mediante
  • SPI se puede abrir para admitir expresiones definidas por el usuario

Ejecución de una condición específica bajo una regla.

La ejecución de una condición bajo una regla es en realidad la ejecución de un operador.

  1. Paso 1: Obtenga los operandos izquierdo y derecho, que deben ejecutarse dinámicamente según el contexto para obtener el valor final
  2. Paso 2: Ejecute la comparación de juicio específica del operador
  3. Paso 3: Obtener el resultado, o como operando para la ejecución del siguiente operador
public abstract class AbstractBaseOperator extends AbstractIdentifiableOperator{
    
    

    /**
     * @param context 引擎执行上下文
     * @return 识别结果
     */
    @Override
    public EvalResult doEval(EngineExecutionContext context) {
    
    
        Variable op1 = operands.get(0);
        Variable op2 = operands.get(1);
        if (op1 == null || op2 == null) {
    
    
            throw new IllegalArgumentException("argument in  operator can't  be null.");
        }


        Object a, b = null;
        a = op1.eval(context);

        //op2.eval()是个耗时操作。如果op1是Unknown,op2.eval就没必要做了。
        if (a == EvalResult.Unknown) {
    
    
            return EvalResult.Unknown;
        }

        if (op2 != null) {
    
    
            b = op2.eval(context);
        }
        if (b == EvalResult.Unknown) {
    
    
            return EvalResult.Unknown;
        }

        return invokeOperator(a, b, context);
    }


    private EvalResult invokeOperator(Object a, Object b, EngineExecutionContext context) {
    
    
        try {
    
    
            // 第一遍求值时,忽略高耗时操作符
            return EvalResult.valueOf(apply(a, b, context));
        } catch (Throwable e) {
    
    
            throw e;
        } finally {
    
    

        }
    }


    /**
     * 操作符具体逻辑
     *
     * @param a       左操作实参
     * @param b       右操作实参
     * @param context 上下文
     * @return true/false
     */
    protected abstract Boolean apply(Object a, Object b, EngineExecutionContext context);

    @Override
    public void accept(EvaluableVisitor visitor) {
    
    
        visitor.visit(this);
    }

Obtenga el resultado final después de ejecutar la regla

inserte la descripción de la imagen aquí

Descripción: Durante el proceso de ejecución de la regla, se puede registrar el estado de ejecución de cada condición: mucho tiempo, anormal, problemas de acceso, etc. En los resultados devueltos, también se puede registrar para facilitar la resolución de problemas posteriores.

Supongo que te gusta

Origin blog.csdn.net/qq_26437925/article/details/131339489
Recomendado
Clasificación