2015-12-17 53 views
1

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

我看起来是这样的:

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; 
     } 
    } 
} 

这里的问题是,当我使用这样的结构,构造快速度growns非常大与发送通知的所有不同的方法。

我真的不喜欢这个,它使单元测试这个选择单元不可行。

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

是否有某种模式可以达到同样的效果,但效果更好,更清洁?

回答

3

我会建议您结合您的IEmailISms接口为IMessageService(前提是不违反Liskov Substitution Principal),并使用strategy pattern,使您的通知服务,可以选择的类型(或种)使用的是IMessageService

重构到IMessageService

public interface IMessageService 
{ 
    void Send(string subject, string body); 
    bool AppliesTo(IEnumerable<string> 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<string> 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<string> providers) 
    { 
     return providers.Contains("sms"); 
    } 
} 

实施战略格局

public interface IMessageStrategy 
{ 
    void Send(string message, string body, string provider); 
    void Send(string message, string body, IEnumerable<string> 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<string> providers) 
    { 
     foreach (IMessageService messageService in messageServices) 
     { 
      if (messageService.AppliesTo(providers)) 
      { 
       messageService.Send(message, body); 
      } 
     } 
    } 
} 

使用

在您的DI容器,注册匹配IMessageService被解析为一个数组,所有类型。例如,在结构图中:

container.For<IMessageService>().Use<EmailMessageService>(); 
container.For<IMessageService>().Use<SmsService>(); 

或者,您也可以使用扫描来自动拾取在事后添加的新类型。

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

无论哪种方式,与容器注册类型是所有你需要满足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"); 
    } 
} 

请注意,如果你采用这种方法,您EmailStrategy类并不需要改变,如果您稍后决定添加或删除IMessageService - 你只需要改变DI配置。