避免策略设计模式中的服务定位器
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);
}
看看那个伪代码
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);
}