我有一个 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)时才需要logicalHost 名称,因此在 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 容器自动或干净地解决?