具有非依赖性参数的构造函数注入

Dan*_*rth 10 .net c# architecture dependency-injection

我有这样的界面ITradingApi:

public interface ITradingApi
{
    IOrder CreateOrder(...);
    IEnumerable<Symbol> GetAllSymbols();
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这是交易软件供应商的不同API的外观.我的视图模型在其构造函数中依赖于此交易API:

public class MainViewModel
{
    public MainViewModel(ITradingApi tradingApi) { /* ... */ }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

我使用Ninject作为IoC容器,因此我将创建一个我的视图模型的实例,如下所示:

var vm = kernel.Get<MainViewModel>();
Run Code Online (Sandbox Code Playgroud)

现在,我的问题:

执行ITradingApi可能需要额外的参数才能工作.
例:

  • 一个供应商API在内部使用TCP/IP,因此我需要一个主机名和一个端口.
  • 另一个供应商使用COM对象.在这里,我不需要任何信息.
  • 第三个供应商需要帐户的用户名和密码.

本着不允许不完整对象的精神,我将这些作为参数添加到具体实现的构造函数中.

现在,我不确定,这将如何运作.显然,这些附加参数不属于接口,因为它们特定于每个实现.
另一方面,这些附加参数需要由最终用户输入然后传递给实现ITradingApi,这意味着用户ITradingApi需要对具体实现有深入了解.
如何解决这个难题?

更新:
一种方法可能是创建一个ITradingApiProvider公开所需参数列表.View可以自动为这些参数创建一个输入表单,该表单将数据绑定到参数中ITradingApiProvider.现在,当ITradingApi从提供程序请求实例时,它可以使用这些参数来创建具体实现的实例.显然执行ITradingApiProviderITradingApi紧密耦合,但我认为只要每个实现ITradingApi都带有相应的实现,这不是问题ITradingApiProvider.

Mar*_*ann 3

根据目前提供的信息,我想指出一两件事:

首先,具体的配置值是否在组合时提供,或者在运行时作为用户输入真正首先可用,会产生巨大的差异。只要它们可以在组合时解决,事情就很容易,因为您可以简单地从环境中读取值并将它们提供给适当的构造函数。因此,对于这个答案的其余部分,我将假设事情要困难得多,并且您实际上需要在运行时从用户那里获取这些值。

我宁愿对实际发生的情况进行建模,而不是尝试提出通用配置 API。在这种情况下,我觉得我们正在从用户那里收集配置值,那么为什么不明确地对此进行建模呢?

产品交易员

定义一个这样的接口:

public interface ITradingApiTrader
{
    ITradingApi Create(Type apiType);
}
Run Code Online (Sandbox Code Playgroud)

这里,假设apiType可以转换为 ITradingApi,但这不能由编译器强制执行。(我将其称为“交易者”的原因是因为这是产品交易者模式 (PLoPD 3) 的变体。)

这和以前有什么不同?

那么,您可以通过显示每种类型的 ITradingApi 的用户界面来实现Create 方法。每个具体用户界面都会收集其自己的具体 ITradingApi 实现所需的值,然后返回正确配置的实例。

如果您在编译时知道具体类型,其他变体包括:

public interface ITradingApiTrader
{
    ITradingApi CreateMT4TradingApi();

    ITradingApi CreateFooTradingApi();

    ITradingApi CreateBarTradingApi();

    // etc.
}
Run Code Online (Sandbox Code Playgroud)

也许你也可以这样做(虽然我还没有尝试编译这个):

public interface ITradingApiTrader
{
    ITradingApi Create<T>() where T : ITradingApi;
}
Run Code Online (Sandbox Code Playgroud)

另请注意,您不需要基于类型定义第一个 ITradingApiTrader 的 Create 方法 - 任何标识符(例如枚举或字符串)都可以代替。

游客

如果 ITradingApi 集在设计时是(有限且)已知的,则访问者设计模式也可能提供替代方案。

如果您使用访问者,则可以使 Visit 方法显示适当的用户界面,然后使用从用户界面收集的值来创建适当的 ITradingApi 实例。

基本上,这只是之前“解决方案”的变体,其中产品交易者作为访问者实现。