SmartParam comes with bulitin dynamic function invokers. Function is a pluggable piece of code that can be invoked with any arguments and return any type of data. Functions are ideal tool to model and store policies or algorithms that either have to be versioned or are applied differently depending on circumstances.
To invoke function use ParamEngine callFunction
or callEvaluatedFunction
. Former simply calls function of given name
with given arguments. Second one first evaluates parameter and interpres parameter output value as a name of
function to call.
To create custom implementation of function you need to implement:
org.smartparam.engine.code.function.Function
is an interface representing function. It implementations can define as
many custom properties as necessary. Function object is loaded from repository and passed on to invoker. Function interface
requires function to have a name (which is unique repository-wide) and type (which determines repository and invoker).
Function repository (org.smartparam.engine.core.function.FunctionRepository
) is responsible for function discovery
and loading. Function repository does not have a name, instead it is registered with name of functions type that it stores.
To automatically register function repository, annotate it with org.smartparam.engine.annotated.annotations.ParamFunctionRepository
.
Function invoker (org.smartparam.engine.core.function.FunctionInvoker
) is responsible for running the function based
on information it got from implementation of Function
interface. This is the place where all type-specific magic takes
place (like evaluating Groovy script or retrieving Spring bean to call method on it). Function invoker is identified
by type of functions it is able to invoke. To automatically register function invoker, annotate it with
org.smartparam.engine.annotated.annotations.ParamFunctionInvoker
.
As an example let's assume we have two categories of customers. Premium customers pay a lot for their status and so
have other discounting algorithm. Second category is all the rest. Discounting policies have already been
coded (ignore @JavaPlugin
for now):
public class DiscountingPolicies() {
@JavaPlugin("policy.premium")
public long premium(long discountA, long discountB) {
return discountA + discountB;
}
@JavaPlugin("policy.other")
public long other(long discountA, long discountB) {
// take lower
return discountA > discountB ? discountB : discountA;
}
@JavaPlugin("policy.otherPromotion")
public long otherPromotion(long discountA, long discountB) {
// take higher
return discountA > discountB ? discountA : discountAB;
}
}
Business also wants to introduce special offer periods, when discounts are calculated differently.
This would be quite complicated if not for SmartParam. Notice the @JavaPlugin
annotation - this registers annotated method
as Java function under given name. If we already registered functions, let's construct parameter discountPolicy
that
will encapsulate this business logic.
date | type | policy |
---|---|---|
* | PREMIUM | policy.premium |
*:2013-10-31 | OTHER | policy.other |
2013-11-01:2013-11-30 | OTHER | policy.otherPromotion |
2013-12-01:* | OTHER | policy.other |
And use it:
long calculateFinalDiscount(Date currentDate, Customer customer, long discountA, long discountB) {
return (long) paramEngine.callEvaluatedFunction("discountPolicy",
new LevelValues(currentDate, customer.type()), discountA, discountB);
}
What just happened? ParamEngine function callEvaluatedFunction
first evaluates given parameter using provided context.
Parameter should return single string value, being function name. It is immediately called with arguments provided and
function invocation value is returned.
This example purposely avoids using parameter evaluation context with level creators. To see how to implement it in more robust way read about level creators and evaluation context.