【问题标题】:How to turn this Service Locator pattern into true Dependency Injection pattern?如何将此服务定位器模式转变为真正的依赖注入模式?
【发布时间】:2012-02-21 06:09:53
【问题描述】:

我在一分钟前问了一个更普遍的问题:How to organize DI Framework usage in an application?,我得到的反馈是我使用的是服务定位器模式,而不是 Martin Fowler 在此处指出的真正的 DI:http://martinfowler.com/articles/injection.html

实际上,我前几天刚读过那篇文章,但显然还没有完全掌握。

假设我有以下代码:

interface ICardReader
{
    string GetInfo();
    void SetDebugMode(bool value);
    void Initialize(string accountToken);
    void ShowAmount(string amount);
    void Close();

    ICreditCardInfo GetCardInfo();
}

public class MagTekIPAD: ICardReader
{
    public ICreditCardInfo GetCardInfo()
    {
        var card = GetCardDataFromDevice();

        // apparently the following line is wrong?
        var ccInfo = Inject<ICreditCardInfo>.New(); 

        ccInfo.Track1 = MakeHex(card.EncTrack1);
        ccInfo.Track2 = MakeHex(card.EncTrack2);
        ccInfo.MagSignature = MakeHex(card.EncMP);
        ccInfo.MagSwipeKeySN = MakeHex(card.KSN);
        ccInfo.MagSignatureStatus = MakeHex(card.MPSts);
        ccInfo.MagDeviceSN = ipad.Serial;
        ccInfo.MSREncryptType = "MAGENSA_V5";

        return ccInfo;
    }

    // Other implementation details here ...
}

在这个例子中,我可以将依赖注入到构造函数中——我认为这是修复“这种”场景的正确方法。

但是,如果我实际上需要创建未知数量的相关对象(或者有任何其他正当理由我需要在类中动态创建依赖项)怎么办?

【问题讨论】:

标签: c# .net dependency-injection inversion-of-control ioc-container


【解决方案1】:

这个例子给我的印象是您尝试使用 IoC 容器创建一个命名为 ICreditCardInfo 的数据传输对象。此类对象不应像服务那样​​具有任何真正的依赖关系。创建 DTO 的正确方法是使用 new 运算符:

return new CreditCardInfo(
        MakeHex(card.EncTrack1),
        MakeHex(card.EncTrack2),
        MakeHex(card.EncMP),
        MakeHex(card.KSN),
        MakeHex(card.MPSts),
        ipad.Serial,
        "MAGENSA_V5");

【讨论】:

  • 谢谢。我认为最好限制依赖关系,如果我在这里创建一个新的 CreditCardInfo,那么我必须依赖于包含该类的程序集。所以有两个问题:1)你是说应该完全没问题吗? 2) 如果 CreditCardInfo 中有一些方法可以做一些事情,那么这会改变你的答案吗?
  • @BrandonMoore 1) 如果没有理由将 CreditCardInfo 放在不同的程序集中,那么我不会这样做。通常,接口在程序集而不是实现之间共享。 2) CreditCardInfo 对我来说似乎是一个合适的 DTO。因此,我不会在那里添加功能,而是提供一些提供功能并将 ICreditCardInfo 作为输入的服务。但我不知道你的系统。如果有充分的理由在那里添加功能并且它需要真正的依赖关系,那么可以 - 在这种情况下,您必须使用 IoC 容器来创建它
  • 谢谢,我真的很感激你们都向我提供了关于您认为它应该如何工作的信息,同时也没有忽略直接回答所提出的问题。我希望每个人都能学会用这种方式回答问题,因为答案的两面对我来说都同样有价值。
【解决方案2】:

ICreditCardInfo对象的工厂注入MagTekIPAD的构造函数

public class MagTekIPAD : ICardReader
{
  private readonly Func<ICreditCardInfo> factory;
  public MagTekIPAD(Func<ICreditCardInfo> factory)
  {
    this.factory = factory;
  }
  public ICreditCardInfo GetCardInfo()
  {
    var info = factory();
    // ...
    return info;
  }
}

如果多个容器知道如何创建T 的实例,则它们可以自动生成Func&lt;T&gt; 代表,因此您不必定义工厂接口或抽象工厂类。

【讨论】:

  • 谢谢......我仍然在思考如何做到这一点,但一切都已经变得更有意义了,所以我想在 24 小时内我会想知道这一切有什么难的 :)
  • 虽然我不喜欢将Func代表用作工厂,但我完全同意使用工厂的一般建议。
  • @Steven 如果您更喜欢更明确的工厂定义但不想实现它们,则可以使用Castle Windsor Typed Factory Facilities 及其port to Microsoft Unity。定义工厂接口并告诉容器完成剩下的工作。
  • @SebastianWeber:我宁愿手动定义一切 :-)。没有什么比一个简单的ICreditCardInfoFactory 更清晰和干净了。
  • @Steven 我并不是说委托比明确的工厂定义更可取。如果我的基础架构可以为我解决这些问题,我就不会费心去实现这些接口。
【解决方案3】:

正如 Fowler 指出的,Service Locator 是更直接的方法,更不容易出错。

一些依赖注入框架要求你声明你是在处理Singleton,或者它们可以有不同的lifetimes

【讨论】:

  • -1 ServiceLocator 是considered an anti-pattern in modern application architectures。它隐藏了依赖关系,因此不仅更容易出错,而且更容易出错。
  • 我给了你一个 +1 来抵消反对者只是因为我不完全相信服务定位器是一个如此可怕的东西。我认为使用这种模式更容易开发,而且人们通常会为了其他感知到的好处或从未像希望的那样实现的好处而轻易放弃这种好处。
  • 相反,我意识到这不是某些场景的最佳模式,但我认为对于许多其他场景,从长远来看,DI 不会比 Service Locator 更好。
  • 谢谢。每个抽象层都有一定程度的复杂性。我使用过由于过度抽象而模糊了意图的代码。当涉及到 setter-injection 时,DI 也好不到哪里去。一些实现使用类上的注释/属性来声明这些依赖关系。这些可以确定依赖项已正确连接。
  • 属性注入应该保留给“很高兴拥有”的依赖项。如果您的代码在没有依赖项的情况下无法运行,那么构造函数注入应该是可行的方法。您可以大致了解运行代码所需的内容。没有隐藏的惊喜会让你以后失败。以及DI attributes is another approach that is strongly disencouraged的使用。
猜你喜欢
  • 2015-03-07
  • 1970-01-01
  • 2016-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多