dependency injection类型选择

最近我遇到了一个问题,我必须根据参数选择一个类型。 例如:用于发送应根据输入参数选择正确频道(电子邮件,短信,…)的通知的类。

我看起来像这样:

public class NotificationManager { IEmail _email; ISms _sms; public NotificationManager (IEmail email, ISMS sms) { _email = email; _sms = sms; } public void Send(string type) { switch(type) { case "email": _email.send; break; case "sms": _sms.send; break; } } } 

这里的问题是,当我使用这种结构时,构造函数会随着发送通知的所有不同方法快速增长。

我真的不喜欢这个,它使单位测试这个选择单元无法管理。

我不能简单地说new email(); 因为通知类型的电子邮件将依赖于IEmailManager,这只会移动问题。

是否有某种模式可以做同样的事情,但是以更好,更清洁的方式?

我建议您将IEmailISms接口合并到IMessageService (假设不违反Liskov Substitution Principal )并使用策略模式使您的通知服务能够选择IMessageService的类型(或类型)使用的。

重构为IMessageService

 public interface IMessageService { void Send(string subject, string body); bool AppliesTo(IEnumerable providers); } public class EmailMessageService : IMessageService { public EmailMessageService(/* inject dependencies (and configuration) here */) { // Set dependencies to private (class level) variables } public void Send(string subject, string body) { // Implementation - use dependencies as appropriate } public bool AppliesTo(IEnumerable providers) { return providers.Contains("email"); } } public class SmsMessageService : IMessageService { public SmsMessageService(/* inject dependencies (and configuration) here */) { // Set dependencies to private (class level) variables } public void Send(string subject, string body) { // Implementation - use dependencies as appropriate } public bool AppliesTo(IEnumerable providers) { return providers.Contains("sms"); } } 

实施战略模式

 public interface IMessageStrategy { void Send(string message, string body, string provider); void Send(string message, string body, IEnumerable providers); } public class MessageStrategy : IMessageStrategy { private readonly IMessageService[] messageServices; public MessageStrategy(IMessageService[] messageServices) { if (messageServices == null) throw new ArgumentNullException("messageServices"); this.messageServices = messageServices; } public void Send(string message, string body, string provider) { string[] providers = provider.Split(';').Select(p => p.ToLower().Trim()).ToArray(); this.Send(message, body, providers); } public void Send(string message, string body, IEnumerable providers) { foreach (IMessageService messageService in messageServices) { if (messageService.AppliesTo(providers)) { messageService.Send(message, body); } } } } 

用法

在您的DI容器中,注册与IMessageService匹配的所有类型以作为数组进行解析。 例如,在StructureMap中:

 container.For().Use(); container.For().Use(); 

或者,您也可以使用“扫描”自动拾取新类型,这些类型会在事后添加。

 var container = new Container(x => x.Scan(scan => { scan.TheCallingAssembly(); scan.WithDefaultConventions(); scan.AddAllTypesOf(); })); 

无论哪种方式,使用容器注册类型都是满足IMessageService[]依赖关系所需的全部内容。

然后,只需将IMessageStrategy注入需要消息传递并传递魔术字符串以选择要使用哪种类型的消息服务的类。

 public class SomeService : ISomeService { private readonly IMessageStrategy messageStrategy; public SomeService(IMessageStrategy messageStrategy) { if (messageStrategy == null) throw new ArgumentNullException("messageStrategy"); this.messageStrategy = messageStrategy; } public void DoSomething() { // Send a message via email this.messageStrategy.Send("This is a test", "Hello", "email"); // Send a message via SMS this.messageStrategy.Send("This is a test", "Hello", "sms"); // Send a message via email and SMS this.messageStrategy.Send("This is a test", "Hello", "email;sms"); } } 

请注意,如果采用此方法,如果您稍后决定添加或删除IMessageService则无需更改EmailStrategy类 – 您只需更改DI配置即可。