依赖注入模式设计的基本原则是什么?

3
我对全DI模式的概念还很陌生,有一些基本的设计疑问。 我正在使用Unity应用程序块2.0作为我的DI框架。
针对这些问题:
1. 假设我有一个名为IDevice的硬件设备接口,以及一些接收此IDevice的硬件监听器。 现在假设你有几个实现了IDevice接口的硬件设备和几个监听器。 你需要为每个监听器指定要注入的实际设备。 你不能只将单个设备映射到接口,你需要类似多重映射的东西。
嗯,可能的一个解决方案是为每个实际设备创建另一层抽象,例如
public interface IActualDevice : IDevice { }

public class ActualDevice : IActualDevice { }

public class SimulatedActualDevice : IActualDevice { }

public class OtherAcualDevice : IOtherAcualDevice { }

那么就有可能创建这样一种映射:
container.RegisterType<IActualDevice, ActualDevice>()

如果硬件丢失:
container.RegisterType<IActualDevice, SimulatedActualDevice>()

所以你觉得这个设计怎么样?
2. DI模式为我们提供了良好的对象创建机制。
那粘合剂呢?对象之间的事件订阅呢?
难道你不觉得它缺少了一些东西,或者说我错过了一些支持它的Unity功能吗?
2个回答

3

不需要引入标记接口来使您的DI容器工作 - 这将是一个漏洞的抽象。

使用Unity,您可以像这样为每个Listener配置其自己的IDevice实现:

container.RegisterType<IDevice, ActualDevice>("actual");
container.RegisterType<IDevice, OtherActualDevice>("otherActual");

container.RegisterType<IListener, Listener1>("listener1",
    new InjectionConstructor(
        new ResolvedParameter<IDevice>("actual")));
container.RegisterType<IListener, Listener2>("listener2",
    new InjectionConstructor(
        new ResolvedParameter<IDevice>("otherActual")));

现在您可以像这样解决监听器:
var listener1 = container.Resolve<IListener>("listener1");
var listener2 = container.Resolve<IListener>("listener2");

通常,DI 的核心模式是构造函数注入抽象工厂。其他大部分内容都源于这两个模式。有关更多 DI 模式和原则,请参见此答案


这正是我在第一个问题上寻找的答案。我建议使用“泄漏抽象”(Leaky abstraction),虽然知道它很丑,但只为了讨论,以澄清我的观点。至于我的第二个问题,我想到了一个AppFactory,它将生产“胶水”——所有事件之间的事件订阅。你认为呢? - Adiel Yaacov
你可能想看一下领域事件:http://msdn.microsoft.com/zh-cn/magazine/ee236415.aspx - Mark Seemann

-2

1) 这是一个非常糟糕的想法,因为接口旨在隐藏实现细节。在任何地方使用IDevice类型,并手动注入此依赖项。

IDevice d;

if(a == 1)
{
   d = container.Resolve<SimulatedActualDevice>()
}
else
{
   d = container.Resolve<ActualDevice>()
}

User user = container.Resolve<IUser>();
user.Device = d;

如果您不想在代码中使用实现类名称,则可以使用命名注册:

IDevice d;

if(a == 1)
{
   d = container.Resolve<IDevice>("Simulated")
}
else
{
   d = container.Resolve<IDevice>("Actual")
}

User user = container.Resolve<IUser>();
user.Device = d;

2) 对象之间的事件订阅是对象初始化的一部分,而不是创建的一部分。Unity只是封装了“new”操作符。在构造函数或像使用DI容器之前那样在特殊工厂中进行订阅。


3
这个回答同时推荐了“紧耦合”和“服务定位器反模式”——两种非常糟糕的想法。 - Mark Seemann
紧耦合仅用作示例,实际上在“策略”模式中并不是一个坏主意。至于服务定位器,请建议另一种方法。最常见的例子是如何动态创建不同策略的对象? - Yauheni Sivukha
是的,你将如何实现这样的抽象工厂?像在https://dev59.com/iHI-5IYBdhLWcg3wSGUB#1927167中手动创建对象吗?在这种情况下,你的抽象工厂将有许多不必要的依赖项。在实现该工厂时,使用紧密耦合和服务定位器会更好。 - Yauheni Sivukha
请看我在那个问题下的评论。 - Mark Seemann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接