简体   繁体   中英

Dynamic condition based decision model

I have an xml file which contains conditions to be checked against a metadata JSON and value data JSON input.

Sample metadata JSON input can be

{
"max_cash":789,
"min_cash":12,
"start_date":"28 Oct 2018",
"end_date":"02 Dec 2018",
"allowed_cities":"3,4,5,6"
} 

Sample value data JSON can be

{
"cash":34,
"order_date":"03 Nov 2018",
"city_id":"5,4"
}

Each condition can form an expression and has following attribute

  • param_holder - this is left term of expression. Its just a key whose value is in metadata JSON.
  • param_value_holder - this is right term of expression. Its just a key whose value is in value data JSON.
  • operator - which operator to be performed on left and right term. For example <,>,<=,>=,<>,==,CONTAINS, NOT_CONTAINS, CONTAINS_AT_LEAST_ONE etc.
  • param_type - data type of left and right term . For example NUMBER, DATE, TIME, STRING, LIST.

Sample xml looks like this

<condtion id="1" param_type="NUMBER" param_holder="max_cash" operator=">=" param_value_holder="cash" next_condition="2"></condtion>
<condtion id="2" param_type="NUMBER" param_holder="min_cash" operator="<=" param_value_holder="cash" next_condition="3"></condtion>
<condtion id="3" param_type="DATE" param_holder="start_date" operator="<=" param_value_holder="order_date" next_condition="4"></condtion>
<condtion id="4" param_type="DATE" param_holder="end_date" operator=">=" param_value_holder="order_date" next_condition="5"></condtion>
<condtion id="5" param_type="LIST" param_holder="allowed_cities" operator="CONTAINS" param_value_holder="city_id" next_condition="-1">

I am working with JAVA, and almost everything here is dynamic which can be added , modified and removed anytime. In future there might by some more operators, data types etc. I am looking for a solution where people can add any conditions without re deploying the code. Code deployment should be done only if metadata JSON changes. I read about ANTLR and it think it will over killing the solution and creation and maintenance of grammar and parser is overhead for a developer. I came up with another solution with Factory Pattern, where Operator Factory takes input as operator and returns respective Operator object which further takes left term, right term and param type as parameter and evaluated the condition based on specific param type.

public abstract class Operator {

    public abstract boolean evaluate(String leftTerm, String rightTerm, String param_type) throws ParseException;

}

public class GTEOperator extends Operator {
    @Override
    public boolean evaluate(String leftTerm, String rightTerm, String param_type) throws ParseException {
        switch (param_type)
        {
            case "DATE":
                return new SimpleDateFormat().parse(leftTerm).after(new SimpleDateFormat().parse(rightTerm));
            case "NUMBER":
                return Integer.parseInt(leftTerm) >=Integer.parseInt(rightTerm);
            default:
                return true;
        }
    }
}

public class ContainsOperator extends Operator {
    @Override
    public boolean evaluate(String leftTerm, String rightTerm, String param_type) throws ParseException {
        return Arrays.asList(leftTerm.split(",")).contains(rightTerm);
    }
} 

This solution is not dynamic and involves a lot of code rewrite. Can some one help me with a better, extensible and object oriented approach.

I would suggest some wiser funcitonal approaches here.

First of all, get rid of next_condition . Since adjunction is commutative (ie booleanValue1 && boleanValue2 will always be equal to the booleanValue2 && booleanValue1 ), you simply don't care about certain order of evaluation .

Secondly, you'd better map each condition to a.. predicate. Function, taking your to-be-validated object and returning single true/false in the simplest case or something more sophisticated (like an array of meaningful errors and whatever esle you might require).

As long as you got those predicates, you are free to merge (reduce, fold, aggregate - there are lots and lots of names for essentially the same function) them into a single one: decide, wether you'd prefer lazy or eager evaluation strategy and either produce yet another predicate or compute them on the fly and spit out resulting Boolean value. Thus, your "client code" recieves a single ToBeValidateObject -> Boolean function, nicely encapsulating all the underlying details .

Finally, you definitely need some kind of factory to produce a valid predicate based on the kind of operation specified. That's your maing extension point: intorudcing new operators or redefining old ones should be as simple as modifying your Factory.Create(...) method. Make sure there is IFactory interface to avoid static not-unit-testable code dependencies. Internally that may be as simple as a HashTable, keyed by appropariate string/char array.

Hope that helps. Let me know if you need more code examples.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM