【问题标题】:Is Ninject/DI useful in simple scenarios?Ninject/DI 在简单场景中有用吗?
【发布时间】:2012-12-20 07:23:34
【问题描述】:

在过去的几天里,我观看了很多 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();

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

【问题讨论】:

    标签: dependencies inversion-of-control ninject code-injection


    【解决方案1】:

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

    【讨论】:

    • 我喜欢它,尤其是 IOC 如何为我提供所有通知者的列表。有趣的是,当我在谷歌上搜索为什么从 IOC 获得 DTO 是不好的做法时,我又看到了你的名字 :-)
    【解决方案2】:

    DI 的主要优点是可维护性。通过让 DI 为您注入您的依赖项,就像在代码中的统一位置配置的那样,随着程序的发展,更容易换入和换出不同的类。因为这些依赖关系通常基于接口,所以这会强制松散耦合。正如您提到的武器,这个想法是您的不同组件不必“了解”彼此即可正常运行。

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

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

    综上所述,根据我的个人经验,DI 并不是适用于所有场景的灵丹妙药。 DI 增加了一层复杂性以换取一层抽象。这对应用程序架构的健壮性有很大的好处,但也可能是不必要的。这篇关于 SO What is dependency injection? 的文章对 DI 的用处进行了一些讨论。这个答案https://stackoverflow.com/a/140655/1068266 我认为以相当合理的方式总结了 DI。

    简而言之,我不相信为了 DI 而实施 DI。阅读这篇文章http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html,它在我上面提到的帖子中被引用。这是我发现的对该主题最清晰、最简单的定义。如果您不认为您可以从基于该文章中解释的模式中受益,那么 DI 可能会在您的应用程序中构成不必要的复杂层。

    【讨论】:

      【解决方案3】:

      一般来说,依赖注入是为了免除类必须为自己解决依赖关系的问题,而是让它们依赖一些更高级别的代码来为它们工作。

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

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

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

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

      【讨论】:

        【解决方案4】:

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

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

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

        在我看来,您需要像 factory 而不是 dependency injection 之类的东西来处理您的消息队列。例如:

        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 美元术语。所以这完全取决于你在特定情况下使用什么解决方案

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-01-17
          • 2019-08-06
          • 2011-10-05
          • 2012-04-13
          • 1970-01-01
          • 2011-02-01
          相关资源
          最近更新 更多