1

I have a lot of actions. All actions works with some Object/Context that passed in all actions. I want to use pattern Strategy/Policy.

Here is examples in Kotlin:

interface Action {
    val name: String

    fun run(ctx: Context)
}

class Multiply: Action {

    override name = "MULTIPLY"

    override fun run(ctx: Context) {
        writeToDb(ctx.id, ctx.number * 2)
    }
}

class Substract 

class SendNotification

etc...

So I want to register all strategies on startup. And select strategy from structure like Enum.

val action = selectAwaitingAction()
val ctx = selectCtxById(action.transaction_id)
perfromAction(ctx, actions.getByName(action.name))

fun performAction(ctx Context, action: Action) {
    action.run(ctx)
}

My question is how register strategy by interface type?

Sonique
  • 6,670
  • 6
  • 41
  • 60

1 Answers1

0

Note: This is complete example. If you are looking only for automatic registration by interface type, scroll to last part of answer.

Strategy design pattern can be implemented using function tables. This stores implementations in Map<String,IImpl> where key is name of algorithm and value is concrete implementation of algorithm .

Common approach:

Consider class Context holding all parameters shared between imlementations of interface Solver.

public class Context extends HashMap<String,Object> {
public <T> T get(String key, Class<T> resultClass){
    return resultClass.cast(get(key));
}

public <T> T getOrDefault(String key, T def, Class<T> resultClass){
    return resultClass.cast(getOrDefault(key,def));
}
}

And interface Solver with required methods solve and name

public interface Solver {
void solve(Context context);
String name();
}

Then you can create implementations of Solver interface modifying shared Context object. I have created AddSolver and MultiplySolver in this example.

AddSolver.java:

public class AddSolver implements Solver {

@Override
public void solve(Context context) {
    context.put("result", context.getOrDefault("result",0.0, Double.class) + context.get("add", Double.class));
}

@Override
public String name() {
    return "+";
}
}

MultiplySolver.java

public class MultiplySolver implements Solver {
@Override
public void solve(Context context) {
    context.put("result", context.getOrDefault("result",0.0, Double.class) * context.get("multiply", Double.class));
}
@Override
public String name() {
    return "*";
}
}

Manual construction of Map<String,Solver>:

Implementations of interface Solver can be stored in HashMap<String,Solver>

@Test
public void testCustomFunctionMap(){
    HashMap<String,Solver> functionMap = new HashMap<>();
    functionMap.put("+", new AddSolver());
    functionMap.put("*", new MultiplySolver());

    Context context = new Context();
    context.put("add", 2.0);
    functionMap.get("+").solve(context);
    TestCase.assertEquals(2.0, context.get("result", Double.class));

    context.put("multiply", 3.0);
    functionMap.get("*").solve(context);
    TestCase.assertEquals(6.0, context.get("result", Double.class));
}

Automatic construction of Map<String,Solver>

There is more approaches, if you need costruct Map<String,Solver> automatically. A lot of them is mentioned in this question. I have used org.reflections library.

public class SolverScanner{
static HashMap<String, Solver> functionMap;
static {
        functionMap = new HashMap<>();
        Reflections reflections = new Reflections(SolverScanner.class.getPackage().getName());
        for( Class<? extends Solver> clazz : reflections.getSubTypesOf(Solver.class)){
            try {
                Solver solver = clazz.newInstance();
                functionMap.put(solver.name(), solver);
            } catch (Exception e) {
                throw new IllegalStateException("Cannot construct functionMap",e);
            }
        }
}
public static HashMap<String, Solver> getFunctionMap(){
    return functionMap;
}
private SolverScanner(){}//disable instantiating
}

And usage:

@Test
public void testSolverScannerFunctionMap(){
    HashMap<String,Solver> functionMap = SolverScanner.getFunctionMap();

    Context context = new Context();
    context.put("add", 2.0);
    functionMap.get("+").solve(context);
    TestCase.assertEquals(2.0, context.get("result", Double.class));

    context.put("multiply", 3.0);
    functionMap.get("*").solve(context);
    TestCase.assertEquals(6.0, context.get("result", Double.class));
}
Bedla
  • 4,789
  • 2
  • 13
  • 27