3

在过去的几天里,我看了很多 IOC/DI/ninject 教程和视频,但我仍然不相信我明白了这一点。

在大多数示例中,他们通常会说,如果我们想要剑或手里剑会发生什么,我们需要定义 IWeapon。我们想将实际武器的知识与战士分开。

因此,我们将所需的 IWeapon 注入到 Warrior 中,然后让 Ninject(或其他)为我们提供进入 IWeapon 所需的类(例如剑或手里剑),但随后他们继续创建一个默认绑定,该绑定创建一个单一的绑定IWeapon 之剑。

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

就我而言,我有一条消息正在从队列中读取,它将包含有关发送内容和发送给谁的必要详细信息。

我还有一个接口,它知道如何发送带有 SMS、电子邮件、iphone 等实现的消息。在这种情况下,我无法理解如何使用 DI,而不必在我的代码中的某处放置开关 :-(

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

public class Message
{
    public Message(INotify notify)
    {
        _notify = notify;
    }
    public void Send()
    {
        _notify.Send(this);
    }

    readonly INotify _notify;
    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; }
}


_kernel.Bind<INotify>().To<NotifyEmail>();
//kernel.Bind<INotify>().To<NotifySMS>().Named("sms");
//kernel.Bind<INotify>().To<NotifyAPNS>().Named("iphone");

var msg = _kernel.Get<Message>();
msg.Send();

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

4

4 回答 4

4

The main advantage of DI is maintenability. By letting DI inject your dependencies for you, as configured in a unified location in your code, it is easier to swap in and out different classes as your program evolves. Because these dependencies are usually based on interfaces, this enforces loose coupling. As you mentioned with the weapons, the idea is that your different components don't have to "know" about each other to function correctly.

The biggest advantage to DI is really when it comes to testing. Because DI usually means defining dependencies by interface and not by explicit classes, this makes it easy to stub these dependencies when you are trying to test a specific aspect of your program.

A good example of where this would be useful is if your program is accessing a webservice through a proxy class which may not be reasonable to call during the testing of your application. Instead of accessing the WS via MyProxy, you could use DI to have it access the WS via IMyProxy. You could then create a stub proxy which returns dummy values and allows you to test other components in your application without having direct access to the webservice itself.

All that said, in my personal experience DI is not a magic bullet for every scenario. DI adds a layer of complexity in exchange for a layer of abstraction. This can be a great benefit to the robustness of your application's architecture, but it can also be unnecessary. This article on SO What is dependency injection? has a bit of a discussion of the usefulness of DI. This answer https://stackoverflow.com/a/140655/1068266 I think sums up DI in a fairly reasoned light.

In short, I don't believe in implementing DI for DI's sake. Read this article http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html which is referenced in the post I mentioned above. It is the clearest and simplest definition of the subject that I have found. If you don't think that you can benefit from the pattern based on what is explained in that article, the DI will probably amount to an unnecessary layer of complexity in your application.

于 2012-12-20T07:49:20.633 回答
4

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();
    }
}    
于 2012-12-20T09:26:18.913 回答
3

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

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

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

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

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

于 2012-12-20T07:47:10.817 回答
2

您可以使用Contextual Binding而不是使用Named Binding 。重写您的配置以根据注入目标自动检测要注入的内容:

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

但据我了解,这将部分解决您的问题。

在我看来,您需要类似工厂而不是依赖注入来处理您的消息队列。例如:

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();

如果您必须构建复杂的实现,则INotify可以同时使用依赖注入抽象工厂ninject.extensions.factory。在这种情况下,您可以定义新的工厂接口并定义它的操作方式:

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();

最后一个样本非常复杂,可能是5 美分概念的 25 美元术语。所以这完全取决于你在特定情况下使用什么解决方案

于 2012-12-20T09:06:02.043 回答