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
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.