最近,我遇到了一个必须基于参数选择类型的问题。例如:用于发送通知的类,该类应根据输入参数选择正确的通道(电子邮件,短信,...)。
我看起来像这样:
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,这只会解决问题。
是否有某种模式可以执行相同的操作,但是方式更好,更清洁?
我建议您将IEmail
和ISms
接口组合成一个IMessageService
(前提是不违反Liskov Substitution Principal),并使用策略模式使您的通知服务能够选择所使用的类型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"); } }
请注意,如果采用这种方法,EmailStrategy
则以后决定添加或删除IMessageService
-时,无需更改类-您只需要更改DI配置即可。