Dependencies Ninject/DI在简单场景中有用吗?

Dependencies Ninject/DI在简单场景中有用吗?,dependencies,inversion-of-control,ninject,code-injection,Dependencies,Inversion Of Control,Ninject,Code Injection,在过去的几天里,我看了不少IOC/DI/ninject教程和视频,但我仍然不相信我能理解这一点 在大多数例子中,他们通常会说,如果我们想要一把剑或一把舒利根,我们需要定义IWeapon。我们想把实际武器的知识从战士中分离出来 因此,我们将所需的IWeapon注入到战士中,然后让Ninject(或其他)获得所需的类进入IWeapon(比如剑或shuriken),但他们继续创建默认绑定,创建剑到IWeapon的单个绑定 我们如何告诉它使用哪一个?我们不能使用命名绑定,因为您不能在武器上设置命名绑定,

在过去的几天里,我看了不少IOC/DI/ninject教程和视频,但我仍然不相信我能理解这一点

在大多数例子中,他们通常会说,如果我们想要一把剑或一把舒利根,我们需要定义IWeapon。我们想把实际武器的知识从战士中分离出来

因此,我们将所需的IWeapon注入到战士中,然后让Ninject(或其他)获得所需的类进入IWeapon(比如剑或shuriken),但他们继续创建默认绑定,创建剑到IWeapon的单个绑定

我们如何告诉它使用哪一个?我们不能使用命名绑定,因为您不能在武器上设置命名绑定,然后获取

在我的情况下,我从队列中读取了一条消息,它将包括关于发送内容和发送对象的所需详细信息

我还有一个界面,知道如何通过短信、电子邮件、iphone等实现发送消息。在这种情况下,我无法理解如何使用DI,而不必在代码中的某个地方设置开关:-(

公共接口INotify
{
无效发送(消息消息消息);
}
公共类消息
{
公共消息(INotify通知)
{
_通知=通知;
}
公共无效发送()
{
_通知。发送(本);
}
只读INotify\u notify;
公共字符串类型{get;set;}
公共字符串文本{get;set;}
公共字符串名称{get;set;}
公共字符串编号{get;set;}
公共字符串电子邮件{get;set;}
}
_kernel.Bind().To();
//kernel.Bind().To().Named(“sms”);
//kernel.Bind().To().Named(“iphone”);
var msg=_kernel.Get();
msg.Send();

使实例化所需的类变得容易不是重点吗?

依赖项注入通常是关于免除类必须自己解决依赖项的问题,而是让它们依靠一些更高级别的代码来为它们完成这项工作

依赖项注入的好处是提高了可维护性,通过单一责任实现了简单性,并且是单元测试的重要推动者,因为在测试时可以注入模拟依赖项

像Ninject和其他类似的IOC容器这样的框架通常提供了一种方便的方法来管理存在于应用程序范围级别的依赖关系,其中一个特定的解决方案应用于依赖关系的每个实例

另一方面,您的消息示例处理的是场景驱动的情况,其中每个解决方案都取决于某些条件


在场景驱动的情况下使用依赖项注入仍然很有用,如果依赖项注入仍然会给您带来所有好处。但是正如您所说的,有必要使用switch或if/else结构来提供正确的解决方案。这些条件结构应该驻留在某个更高级别的控制代码中at协调具有依赖项的类

DI的主要优点是可维护性。通过让DI为您注入依赖项,就像在代码中的统一位置配置的那样,随着程序的发展,可以更容易地交换不同的类。因为这些依赖项通常基于接口,这就加强了正如你在武器中提到的,你的想法是,你的不同组件不需要“了解”彼此才能正常工作

DI最大的优势实际上是在测试方面。因为DI通常意味着通过接口而不是显式类定义依赖项,所以当您尝试测试程序的特定方面时,可以很容易地存根这些依赖项

一个很好的例子是,如果您的程序通过代理类访问Web服务,而在应用程序测试期间调用代理类可能不合理。您可以使用DI通过
MyProxy
访问WS,而不是通过
IMyProxy
访问WS。然后您可以创建存根代理它返回虚拟值,并允许您测试应用程序中的其他组件,而无需直接访问Web服务本身

综上所述,根据我个人的经验,DI并不是每个场景的灵丹妙药。DI增加了一层复杂性,以换取一层抽象。这对应用程序架构的健壮性是一个很大的好处,但也可能是不必要的。这篇文章对DI的有用性进行了一些讨论我认为答案是从一个相当合理的角度总结DI

简言之,我不相信为了DI而实现DI。请阅读我在上面提到的文章中引用的这篇文章。这是我找到的关于这个主题的最清晰和最简单的定义。如果你不认为你可以从基于该文章中所解释的模式中获益,DI可能会成为一个unne应用程序中的必要复杂性层。

不使用可以使用的命名绑定。重写配置以自动检测要注入的内容,具体取决于注入目标:

kernel.Bind<INotify>().To<NotifyEmail>();
kernel.Bind<INotify>().To<NotifySms>().WhenInjectedInto<SmsService>();
kernel.Bind<INotify>().To<NotifyAPNS>().WhenInjectedInto<IPhoneService>();

最后一个示例非常复杂,可能是一个5美分概念的25美元术语。因此,在特定情况下使用什么解决方案取决于您。DI是关于将软件组装在一起,而不是解决业务逻辑。在您的场景中,您尝试从IoC容器中解决DTO,这被视为不好的做法

这意味着您必须以不同的方式对应用程序进行建模。例如,以下伪代码将为您提供处理此场景的一种方法:

public interface INotify
{
    string Type { get; }
    void Send(Message msg);
}

public class Message
{
    public string Type { get; set; }
    public string Text{ get; set; }
    public string Name { get; set; }
    public string Number { get; set; }
    public string Email { get; set; }
}

public class MessageProcessor
{
    public MessageProcessor(IEnumerable<INotify> notifiers, IMessageQueue) 
    {
        this.notifiers = notifiers.ToDictionary(n => n.Type, n);  
    }

    public void Process()
    {
        while (!TerminateApplication) 
        {
            var msg = this.queue.GetNextMessage();
            this.notifiers[msg.Type].Send(msg);
        }
    }
}


public void Main() 
{
    using (var kernel = new StandardKernel())
    {
        kernel.Bind<INotifier>().To<NotifyEmail>();
        kernel.Bind<INotifier>().To<NotifySms>();
        kernel.Bind<INotifier>().To<Notify>();
        kernel.Bind<MessageProcessor>().ToSelf();

        kernel.Get<MessageProcessor>().Process();
    }
}    
公共接口INotify
{
字符串类型{get;}
无效发送(消息消息消息);
}
公共类消息
{
公共字符串类型{get;set;}
公共字符串文本{get;set;}
公共字符串名称{get;set;}
公共字符串编号{get;set;}
var factory = new Dictionary<string, Func<Message>>
{
    { "unknow", () => new Message(new NotifyEmail()) },
    { "sms", () => new Message(new NotifySms()) },
    { "iphone", () => new Message(new NotifyAPNS()) }
};

factory["iphone"]().Send();
kernel.Bind<INotify>().To<NotifyEmail>()
        .NamedLikeFactoryMethod<INotify, INotifocationFactory>(f => f.GetNotifyEmail());
kernel.Bind<INotify>().To<NotifySms>()
        .NamedLikeFactoryMethod<INotify, INotifocationFactory>(f => f.GetNotifyEmail());
kernel.Bind<INotify>().To<NotifyAPNS>()
        .NamedLikeFactoryMethod<INotify, INotifocationFactory>(f => f.GetNotifyEmail());

// receive INotifocationFactory using constructor injection,
// do not resolve it directly, because this will led you to ServiceLocator anti-pattern
var abstractFactory = kernel.Get<INotifocationFactory>();

var factory = new Dictionary<string, Func<Message>>
{
    { "unknow", () => new Message(abstractFactory.GetNotifyEmail()) },
    { "sms", () => new Message(abstractFactory.GetNotifyEmail()) },
    { "iphone", () => new Message(abstractFactory.GetNotifyAPNS()) }
};

factory["iphone"]().Send();

kernel.Bind<INotifocationFactory>().ToFactory();
public interface INotify
{
    string Type { get; }
    void Send(Message msg);
}

public class Message
{
    public string Type { get; set; }
    public string Text{ get; set; }
    public string Name { get; set; }
    public string Number { get; set; }
    public string Email { get; set; }
}

public class MessageProcessor
{
    public MessageProcessor(IEnumerable<INotify> notifiers, IMessageQueue) 
    {
        this.notifiers = notifiers.ToDictionary(n => n.Type, n);  
    }

    public void Process()
    {
        while (!TerminateApplication) 
        {
            var msg = this.queue.GetNextMessage();
            this.notifiers[msg.Type].Send(msg);
        }
    }
}


public void Main() 
{
    using (var kernel = new StandardKernel())
    {
        kernel.Bind<INotifier>().To<NotifyEmail>();
        kernel.Bind<INotifier>().To<NotifySms>();
        kernel.Bind<INotifier>().To<Notify>();
        kernel.Bind<MessageProcessor>().ToSelf();

        kernel.Get<MessageProcessor>().Process();
    }
}