【问题标题】:Is this pattern a service locator, or a command factory, or something else?这个模式是服务定位器,还是命令工厂,还是别的什么?
【发布时间】:2016-06-29 18:59:40
【问题描述】:

我有以下课程(在 JEE 中,可以在 Spring 中类似地制作):

@Singleton
public class MyUnknownPatternClass {
    @Inject @Any Instance<SomeInterface> instances;

    public SomeInterface getMatchingInstance(Object someDiscriminator) {
        for(SomeInterface instance : instances) {
            if(instance.supports(someDiscriminator)) {
                return instance;
            }
        }
        throw new IllegalArgumentException("Could not find a matching instace for " + someDiscriminator.toString());
     }
}

使用依赖注入的发现来定位匹配接口的所有实例,这让我可以完全解耦策略和使用它的代码。

例如,对于一个旅行推销员应用程序,我有不同的 TransportProviders 来实现 travel(Location l) 方法,然后我的业务逻辑可以专注于以下常规流程:

Salesman m =...;
Location l =  m.getStartLocation();
TransportProvider t = myUnknownPatternClass.getMatchingInstance(m.getTravelMethod());
for(Location d : m.getDestinationsToVisit())
    l = t.travel(d);
    m.doBusinessHere(l);
}

从而将销售人员的出行方式与步行、乘船、汽车或其他任何方式脱钩。

我的理解是工厂实际上实例化了对象。服务定位器更通用,并允许运行时注册,而上面的代码似乎两者都没有。它不是白板模式,因为它只返回一个实例。

但是,这是一个非常有用的模式,如果能恰当地谈论它会很好。

那么,它是什么?
那么该类的正确名称是什么 (即SomeInterfaceLocator,或SomeInterfaceFactory)?

编辑: 我找到了这个https://en.wikipedia.org/wiki/Command_pattern#Java_8
是命令工厂模式吗?

【问题讨论】:

  • 在我看来就像一个工厂——在给定一组参数的情况下提供预期类型的​​对象。它返回的实例是通过 DI 实例化的这一事实并不会改变它的功能,只会改变它的底层实现。
  • 工厂的“描述”不是暗示返回值是你的(即不共享)吗?因为通常工厂返回可以修改、调整、删除等的唯一对象。这并没有降低(尽管这很困难,因为接口可能不允许这样做)。如果您向其他人描述这一点,您会称其为工厂但不同,如果是,那是什么?
  • 它返回一个实例——消费者无法知道该实例是共享的——它可以通过允许此类行为的任何属性或方法进行修改。注入构造函数的实例可能是新实例,这个对象无法知道或强制执行这一点——它只知道它有一个对象列表。随便你怎么称呼它,但从外面看,它的外观、气味和行为就像一个工厂。

标签: design-patterns dependency-injection factory service-locator


【解决方案1】:

此实现不是工厂,因为它不会创建新实例。

它也不是服务定位器,因为它不定位服务 - 现在这需要几句话解释。

服务定位器应该返回不同的接口。您的方法正在返回实现单个通用接口的对象。这是服务定位器的签名:

T LocateService<T>() { ... }

这是一个可怕的签名,给全世界的开发者带来了如此多的麻烦。问题是该方法的签名并不能说明消费者可能依赖的对象类型。让我用一个更长的例子来展示它:

class ServiceLocator {
    T Locate<T>();
}

class Golum {

    ServiceLocator locator;

    Consumer(ServiceLocator depedency) { ... }

    void beNice() {
        Preciousss myPrecious = this.locator.Locate<Previousss>();
        myPrecious.DoMagic();
    }
}

void main() {
    new Golum(new ServiceLocator()).beNice(); // Fails!
}

这段代码是滥用服务定位器带来的所有邪恶的升华。 Main 函数正在初始化 Golum 类,但它不知道 Golum 需要什么。 Golum 只需要一个服务定位器,任何服务定位器,然后就开始了。当调用 beNice 方法时,它会使传递的特定服务定位器对象根本不知道 Preciousss 类,然后调用 Locate 失败。

归根结底,当一个类没有明确声明其依赖关系时,就会出现负面后果。如果你看看你的情况,这不是那里发生的事情。您的类依赖于 SomeInterface,然后请求解析器对象,该对象也知道 SomeInterface。我没有看到负面后果。

在谈论命名时,我可能会选择将“定位器”对象称为 SomeInterfaceLookup,尽管 SomeInterfaceLocator 不会比这更糟。

【讨论】:

  • 所以我们一致认为它不是服务定位器,也不是工厂,而可能是 Lookup 类。但问题是,这个模式的名称是什么才能正确地向其他人描述它。如果向其他人描述这一点会更容易说它是“某种工厂”还是其他什么?
【解决方案2】:

我想我会使用(Strategy)Selector 作为模式。这不是官方的 100,但它就是这样做的。
除非有人找到更好的名字,否则它是为了表明它不是服务定位器,它不是工厂,也不是命令模式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 2017-03-27
    • 1970-01-01
    相关资源
    最近更新 更多