public interface ITradingApi { IOrder CreateOrder(...); IEnumerable<Symbol> GetAllSymbols(); // ... }
这意味着是贸易软件供应商的不同API的立面.
我的观点模型在它的构造函数中依赖于这个交易API:
public class Mainviewmodel { public Mainviewmodel(ITradingApi tradingApi) { /* ... */ } // ... }
我使用Ninject作为IoC容器,所以我将创建一个我的视图模型的一个实例,如下所示:
var vm = kernel.Get<Mainviewmodel>();
现在,我的问题:
ITradingApi的实现可能需要额外的参数来工作.
例:
>一个供应商API在内部使用TCP / IP,因此我需要一个主机名和一个端口.
>另一个供应商使用COM对象.这里我不需要任何信息.
>第三个供应商需要帐户的用户名和密码.
本着不允许不完整对象的精神,我将这些作为参数添加到具体实现的构造函数中.
现在,我不确定,这将如何工作.显然,这些附加参数不属于接口,因为它们是针对每个实现的.
另一方面,这些附加参数需要由最终用户输入,然后传递给ITradingApi的实现,这意味着ITradingApi的用户需要对具体实现的深入了解.
如何解决这个困境?
更新:
一种方法可能是创建一个显示所需参数列表的ITradingApiProvider. View可以自动为这些参数自动创建一个数据绑定到ITradingApiProvider中的参数的参数.现在,当提供者请求一个ITradingApi实例时,它可以利用这些参数创建具体实现的实例.显然,ITradingApiProvider和ITradingApi的实现是紧密耦合的,但是我认为只要ITradingApi的每个实现都具有相应的ITradingApiProvider实现,这并不是问题.
解决方法
首先,无论具体配置值是在组合时间提供还是在运行时真正首先可用,因为用户输入造成巨大的差异.只要在组合时可以解决问题,事情很简单,因为您可以简单地从环境中读取值并将其提供给适当的构造函数.所以,对于这个答案的其余部分,我将假设事情变得更加困难,实际上需要在运行时从用户那里获得这些值.
而不是试图提出一个通用的配置API,我更愿意模拟实际发生的情况.在这种情况下,听起来像我们正在从用户收集配置值,所以为什么不明确地建模?
产品交易者
定义如下界面:
public interface ITradingApiTrader { ITradingApi Create(Type apiType); }
在这里,假设apiType可以转换为ITradingApi,但这不能由编译器执行. (我称之为“交易者”的原因是因为这是产品交易者模式(PLoPD 3)的变体.)
这与以前有什么不同?
那么您可以通过显示每种类型的ITradingApi的用户界面来实现Create方法.每个具体的用户界面收集自己具体的ITradingApi实现所需的值,然后返回正确配置的实例.
如果您在编译时知道具体类型,其他变体包括:
public interface ITradingApiTrader { ITradingApi CreateMT4TradingApi(); ITradingApi CreateFooTradingApi(); ITradingApi CreateBarTradingApi(); // etc. }
也许你也可以这样做(虽然我没有尝试编译这个):
public interface ITradingApiTrader { ITradingApi Create<T>() where T : ITradingApi; }
还要注意,您不需要根据类型定义第一个ITradingApiTrader的Create方法 – 任何标识符(例如枚举或字符串)可能会做.
游客
如果在设计时,ITradingApi的集合(有限和已知),Visitor的设计模式也可能提供另一种选择.
如果您使用访问者,可以使访问方法显示适当的用户界面,然后使用从用户界面收集的值创建相应的ITradingApi实例.
基本上这只是以前的“解决方案”的一个变体,产品交易者是作为访客实现的.