【问题标题】:Component architecture and the use of a DI container?组件架构和 DI 容器的使用?
【发布时间】:2012-11-30 08:51:53
【问题描述】:

我有一个 FTP 实用程序负责常见的 FTP 内容:放置文件、获取文件等。作为我们基础架构的一部分,我们需要组件从实现 IConfiguration 接口的组件中检索它们的配置,并将它们的条目记录到实现 ILogger 接口的组件。请注意,ftp 主机是通过逻辑主机名(例如 someFtpSite)描述的,该主机名与配置文件中包含主机名、用户 ID 和密码的部分匹配。所以我的接口和类定义是这样创建的:

public interface IFtpUtility
{
    void PutFile(string sourceFileAndPath, string targetFileAndPath, string logicalHost);
}

public class FtpUtility : IFtpUtility
{
    private readonly IConfiguration _configuration;
    private readonly ILogger _logger;

    public FtpUtility(ILogger logger, IConfiguration configuration)
    {
        _configuration = configuration;
        _logger = logger;
    }
    // IFtpUtility implementation follows
}

我使用此签名的意图是仅在需要执行操作(例如 PutFile)时才需要逻辑主机名,因此在 PutFile 方法本身上提供它。

这种布局非常适合与 Unity 等 IoC 容器一起使用。我们将有以下代码:

UnityContainer = new UnityContainer();
UnityContainer.RegisterType<IConfiguration, ConfigurationProvider>();
UnityContainer.RegisterType<ILogger, LoggingProvider>();
UnityContainer.RegisterType<IFtpUtility, FtpUtility>();

当我以后需要使用 FtpUtility 时,我会依赖容器来进行适当的解析:

IFtpUtility ftpUtility = UnityContainer.Resolve<FtpUtility>();

现在一位同事向我建议,logicalHost name 应该是构造函数的参数,因为没有它,任何函数都无法执行,并且它或多或少与 FtpUtility 的特定实例相关联。每次调用我们只会与一个 FTP 服务器通信。所以我很高兴地将构造函数更改为包含逻辑主机名称,这破坏了我的 IoC 容器解析。 请看,虽然 IConfiguration 和 ILogger 可以与特定的实现相关联,但logicalHost 是特定于实例的,直到创建实例时我们才知道它。现在我可以争辩说这样的改变会改变我的 IoC 容器的使用,因此我无法实现它,等等,但这个论点太弱了(我发现)。那么问题来了:

  • 从架构的角度来看,将logicalHost 放在构造函数上是不是一件好事?
  • logicalHost 名称是否是必需的构造参数,没有它,FtpUtility 就无法运行,因此,它应该是构造函数的一部分吗?
  • 有没有办法用 Unity 解决这个问题?如果构造函数包含逻辑主机的第三个参数,是否存在 Resolve() 的重载,这将允许我在解析时提供第三个参数的值,而不是提前将其注册到容器中?
  • 我是否正在尝试解决没有问题的问题?在这种情况下,使用 IoC 容器是否过度杀伤,而不是帮助,使事情复杂化?毕竟,我仍在使用 IoC 模式——只是无法使用 IoC 容器自动或干净地解决它?

【问题讨论】:

  • 可能是笔误,但为什么你解析FtpUtility而不是IFtpUtility

标签: .net oop architecture dependency-injection unity-container


【解决方案1】:

1,2。从抽象的角度来看,在构造函数中包含主机名是合乎逻辑的,也是一个理想的地方。

3,4。在我个人看来,通过统一解析主机名有点矫枉过正,但完全有可能。一个更简单的选择是实例化这是您的统一配置并从标准 app/web.config 中提取主机名,类似于下面(MVC 实现);

 container.RegisterType<IFtpUtility, FtpUtility>(
      new InjectionConstructor(ConfigurationManager.AppSettings["Hostname"]));

这很好地解耦了事情,并且仍然允许您根据需要配置该设置。

【讨论】:

  • 感谢您的好建议,但这种方法是不可能的,因为我们正在处理多个主机并且主机本身是由 FtpUtility 的调用者决定的,因此需要特定的逻辑主机。跨度>
【解决方案2】:

您可以解析IFtpUtilityFactory,而不是直接解析IFtpUtility,其定义如下:

interface IFtpUtilityFactory
{
    IFtpUtility Create(string logicalHost);
}

任何给定的 FTP 实用程序实现都有相应的工厂实现。它实际上是一个具有通用接口的代理构造函数,可以在解析时间之后提供所需的参数。

【讨论】:

  • 这是一个很好的解决方案,它清楚地将 IFtpUtility 解析与特定实例的实例解析区分开来,但是......我们不是在这里构建一个杀死苍蝇的佳能吗?
  • 我想你可以这样看(另一个接口,加上一个类,它只是调用new 的一种奇特方式),但另一方面,我不认为这是一个在这种情况下,大量的代码或额外的复杂性。我不了解 Unity,但它可能具有使实现此模式更容易的功能,类似于 Ninject's providers
猜你喜欢
  • 2011-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 2011-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多