避免策略设计模式中的服务定位器

Avoid Service locator in strategy design pattern

看看那个伪代码

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);
    }
}

由于服务定位器被认为是反模式,在这种情况下如何避免呢? A,B,C 可以有各种依赖关系,这就是为什么我想使用依赖注入的所有好处来实例化它。我可以同时注入 A,B,C 作为 StrategyResolver 的依赖项,但是如果我有 10 个策略呢? StrategyResolver 依赖列表太长了。

如果您正在使用 Spring,您可以自动注入某种类型的所有已知 bean:

private final List<StrategyInterface> strategies;

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

如果这是唯一的构造函数 Spring 将毫无问题地注入实现 StrategyInterface 的所有 bean。当然,这些 bean 需要被 Spring 知道,例如使用 class:

上的 @Named 注释
@javax.inject.Named
public class A implements StrategyInterface {

顺便说一句,为什么不直接命名接口 'Strategy'? 'Interface' 后缀并没有真正添加任何东西。

编辑:要确定应用哪个策略,您可以向策略接口添加一个方法:

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

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

在 StrategyResolver 中,您将拥有如下代码:

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

或更新的样式:

public StrategyInterface resolve(String data) {
    return strategies.stream().filter(s -> s.appliesTo(data)).findAny()
        .orElseThrow(() -> new IllegalArgumentException("No strategy applies to " + data));
}

这里的一个问题是,要避免上述异常,您必须确保策略始终适用。您可以创建一个 'DefaultStrategy' 始终应用并添加为列表中的最后一个策略,例如:

private final List<StrategyInterface> strategies;

public StrategyResolver(List<StrategyInterface> strategies, DefaultStrategy defaultStrategy) {
    this.strategies = new ArrayList<>(strategies);    
    this.strategies.add(defaultStrategy);
}