依赖注入类型选择 - c#

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

我看起来像这样:

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),并使用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匹配的所有类型注册为要解析的数组。例如,在StructureMap中:

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配置即可。

当回复有时是一个对象有时是一个数组时,如何在使用改造时解析JSON回复? - java

我正在使用Retrofit来获取JSON答复。这是我实施的一部分-@GET("/api/report/list") Observable<Bills> listBill(@Query("employee_id") String employeeID); 而条例草案类是-public static class…

改造正在返回一个空的响应主体 - java

我正在尝试使用Retrofit和Gson解析一些JSON。但是,我得到的响应机构是空的。当我尝试从对象中打印信息时,出现NullPointerException。我确保URL正确,并且我也确保POJO也正确。我正在使用jsonschema2pojo来帮助创建POJO类。这是我要解析的JSON{ "?xml": { "@versi…

每个文件合并后添加换行 - python

我有很多类似以下内容的JSON文件:例如。1.json{"name": "one", "description": "testDescription...", "comment": ""} test.json{"name"…

Json到php,json_decode返回NULL - php

我正在用PHP进行JSON解析器的一些API,用于存储有关遗产的信息。我在解析时遇到问题,因为它返回的是NULL值而不是数组或对象。简单的JSON代码可以很好地解析,但是可以这样:{"success":true,"totalCount":1,"data":[{"id":99694…

将ajax的值存储到javascript变量中 - javascript

我有一个php文件,其中我从服务器获取数据。该php文件的输出是一个包含json格式数据的变量。PHP文件:<?php $dbHostName = "localhost"; $dbUserName = "venseld"; $dbUserPass = "wecuuu"; $dbName = &…