I need to implement a feature for a program that calculates metrics based on predefined measures with the requirement that the addition of a new metric (and/or measure) should require minimal code level changes. This is how I have done it:
class Measure { // clas to hold the measures
Integer id;
String name;
Long value;
// necessary getters and setters
}
These measures are retrieved from a MySQL DB.
// the measures stored in an ArrayList of objects of the above Measure class
[
{
"id": 1,
"name": "Displacement",
"value": 200
},
{
"id": 2,
"name":"Time",
"value": 120
},
{
"id":3,
"name":"Mass",
"value": 233
},
{
"id":4,
"name": "Acceleration",
"value": 9.81
},
{
"id": 5,
"name":"Speed of Light",
"value": 300000000
}
]
I need to get metrics such as the following:
Velocity (Displacement/Time)
, Force (Mass * Acceleration)
and Energy (Mass * Speed of Light^2)
I implemented the following JSON in which I have defined the above formulae:
[
{
"title": "Velocity",
"unit": "m/s",
"formula": "( displacement / time )"
},
{
"title": "Force",
"unit": "N",
"formula": "( mass * acceleration )"
},
{
"title": "Energy",
"unit": "J",
"formula": "( mass * speed_of_light * speed_of_light )"
}
]
I then calculate metrics in the following way:
class Evaluator {
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
public Evaluator(List<Measure> measures) {
measures.forEach(measure -> {
String fieldName = measure.getName().replace(" ", "_").toLowerCase(); // convert Speed of Light -> speed_of_light etc
engine.put(fieldName, Double.parseDouble(measure.getValue()));
})
}
public void formula(String formula) throws Exception {
engine.eval("function calculateMetric() { return " + formula +" }");
}
public Object evaluate() throws ScriptException {
return engine.eval("calculateMetric()");
}
}
The above class is used to load each formula into the Script Engine and then calculate the metric value based on the formula provided.
// load JSON file into JsonArray
JsonArray formulae = parseIntoJsonArray(FileUtils.getContent("formulae.json"));
Evaluator evaluator = new Evaluator(measures);
for (Object formula : formulae) {
JsonObject jsonObj = (JsonObject) formula;
evaluator.formula(jsonObj.get("formula").getAsString());
Double metricVal = Double.parseDouble(evaluator.evaluate().toString());
// do stuff with value
}
The code works as expected. I want to know if I am doing anything wrong that could affect the program in the long run/if anything is not best practice/if there is a better way/easier way to accomplish the same.
This question is closely related to a question I posted yesterday. Sort of a second part.