依赖注入类型选择 [英] Dependency injection type-selection

查看:21
本文介绍了依赖注入类型选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Recently I've come accross a problem where I have to select a type based on a parameter. For example: a class used for sending notifications that should select the right channel (email, sms, ...) based on an input-parameter.

我看起来像这样:

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

这里的问题是,当我使用这种构造时,构造函数会随着发送通知的所有不同方法而迅速变大.

The problem here is that when I use this kind of construction, the constructor fastly growns very large with all the different methods of sending notifications.

我真的不喜欢这样,这使得对这个选择单元进行单元测试变得难以管理.

I really don't like this, and it makes unit-testing this selection-unit unmanagble.

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

I cannot simply say new email(); because the notification-type email will rely on IEmailManager, and this will only move the problem.

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

Is there some kind of pattern that will do the same, but in a better, cleaner way?

推荐答案

我建议你将 IEmailISms 接口组合成一个 IMessageService代码>(前提是不违反 Liskov Substitution Principal)并使用策略模式来启用您的通知服务能够选择使用的 IMessageService 的类型.

I would suggest that you combine your IEmail and ISms interfaces into an IMessageService (provided that doesn't violate the Liskov Substitution Principal) and to use a strategy pattern to enable your notification service to be able to select the type (or types) of IMessageService that are used.

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 匹配的类型以解析为数组.例如,在 StructureMap 中:

Usage

In your DI container, register all types that match IMessageService to be resolved as an array. For example, in StructureMap:

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

或者,您也可以使用 Scan 来自动选取事后添加的新类型.

Or alternatively you can use Scan to pickup new types automatically that are added after the fact.

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

无论哪种方式,您只需在容器中注册类型即可满足 IMessageService[] 依赖项.

Either way, registering the types with the container is all you need to satisfy the IMessageService[] dependency.

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

Then it is just a matter of injecting IMessageStrategy into a class that requires messaging and passing the magic string to select which types of message services to use.

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 配置.

Note that if you take this approach, your EmailStrategy class won't need to change if you decide later to add or remove a IMessageService - you only need to change the DI configuration.

这篇关于依赖注入类型选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆