Home > other >  Avoid Service locator in strategy design pattern
Avoid Service locator in strategy design pattern

Time:11-04

Take a look on that pseudocode

class A,B,C implements StrategyInterface
{
    private dep;

    constructor(Dep dep) {
        this->dep = dep;    
    }
}

class StrategyResolver
{
    private locator;

    constructor(ServiceLocator locator) {
        this->locator = locator;
    }
    
    public function resolve(data): StrategyInterface
    {
        if ( xxx ) {
            return locator->get(A);
        } else if ( yyy ) {
            return locator->get(B);
        }
        return locator->get(C);
    }
}

As service locator is considered as anti pattern, how to avoid it in this case? A,B,C can have various dependencies, thats why I would like to instantiate it using all benefits of dependency injections. I could albo inject A,B,C as dependencies of StrategyResolver, but what if I have there like 10 strategies. The StrategyResolver dependency list is then too long.

CodePudding user response:

If you are using Spring you can inject all known beans of a certain type automatically:

private final List<StrategyInterface> strategies;

public StrategyResolver(List<StrategyInterface> strategies) {
    this.strategies = strategies;
}

If this is the only constructor Spring will have no trouble injecting all the beans implementing the StrategyInterface. Of course those beans need to be known to Spring using for example the @Named annotation on the class:

@javax.inject.Named
public class A implements StrategyInterface {

BTW why not just name the interface 'Strategy'? The 'Interface' suffix does not really add anything.

EDIT: to determine which Strategy applies you can add a method to the Strategy interface:

@javax.inject.Named
public class A implements StrategyInterface {

    @Override
    public boolean applies(String data) {
        // just an example
        return "xxx".equals(data);
    }

In the StrategyResolver you would have code like this:

public StrategyInterface resolve(String data) {
    for(StrategyInterface strategy : strategies) {
        if (strategy.applies(data)) {
            return strategy;
        }
    }
    throw new IllegalArgumentException("No strategy applies for "   data);
}

The only gotcha here is that you must ensure that always one strategy applies. You could even create a 'DefaultStrategy' which always applies but then you must add it as the last strategy in the list of strategies.

  • Related