【问题标题】:Avoid Service locator in strategy design pattern [closed]在策略设计模式中避免使用服务定位器 [关闭]
【发布时间】:2021-12-17 20:18:26
【问题描述】:

看看那个伪代码

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 依赖列表太长了。

【问题讨论】:

  • symfony(用于 PHP)有一个 DI 系统,它允许命名服务并传递一个数组作为类的输入,一个一个地声明列表中的项目。 Docs symfony.com/doc/current/components/dependency_injection.html 让我知道这是否可以解决问题,然后我可能会用一个完整的工作示例来写答案

标签: java php design-patterns strategy-pattern service-locator


【解决方案1】:

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

private final List<StrategyInterface> strategies;

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

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

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

顺便说一句,为什么不直接将界面命名为“策略”? “接口”后缀并没有真正添加任何东西。

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

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

【讨论】:

  • 我考虑了依赖列表,但是当我拥有该列表时,如何从该列表中获取适合我的条件的策略?我主要用 PHP 编写,我遵循命名约定 php-fig.org/bylaws/psr-naming-conventions
  • 我通常会在接口中添加一个方法来确定策略是否适用。因此,您的条件列表 xxx, yyy 将分别进入其所属的策略实施,在该确定方法中。
猜你喜欢
  • 2010-10-01
  • 2023-01-14
  • 2014-05-26
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
  • 2016-08-06
  • 2011-09-27
  • 1970-01-01
相关资源
最近更新 更多