【发布时间】:2017-05-28 22:08:56
【问题描述】:
我一直在从事一个个人项目,除了制作对自己有用的东西之外,我还尝试将其用作继续寻找和学习建筑课程的一种方式。一个这样的教训就像在自行车道中间出现的科迪亚克熊一样,我一直在努力解决它。
这个问题本质上是依赖注入、程序集解耦和实现隐藏(即使用内部类实现我的公共接口)交叉点的问题的混合体。
在我的工作中,我通常发现应用程序的各个层都有自己的接口,它们公开公开,但在内部实现。每个程序集的 DI 代码将内部类注册到公共接口。此技术可防止外部程序集更新实现类的实例。但是,我在构建此解决方案时一直在阅读的一些书籍对此表示反对。与我之前的想法冲突的主要事情与 DI 组合根有关,以及应该在哪里保留给定实现的接口。如果我将依赖注册移动到单个全局组合根(正如 Mark Seemann 建议的那样),那么我可以摆脱每个程序集必须运行自己的依赖注册。然而,缺点是实现类必须是公共的(允许任何程序集实例化它们)。至于解耦程序集,Martin Fowler 指示将接口与使用接口的代码一起放入项目中,而不是实现它的代码。例如,这是他提供的图表,相比之下,我通常如何实现相同的解决方案的图表(好吧,这些并不完全相同;请关注箭头并注意实现箭头何时交叉装配边界而不是合成箭头)。
马丁风格
我平时看到的
我立即在 Martin 的图表中看到了优势,它允许将较低的程序集换成另一个程序集,因为它有一个在其上层实现接口的类。然而,我也看到了这个看似主要的缺点:如果你想从上层换出程序集,你实际上是在“偷走”下层正在实现的接口。
在考虑了一会儿之后,我决定在两个方向上完全解耦的最佳方法是在它们自己的程序集中使用指定层之间契约的接口。考虑这个更新的图表:
这很疯狂吗?开对了吗?对我来说,这似乎解决了接口隔离的问题。但是,它并没有解决无法将实现类隐藏为内部的问题。那里有什么合理的可以做的吗?我不应该担心这个吗?
我脑子里想的一个解决方案是让每一层实现代理层的接口两次;一次是公共类,一次是内部类。这样,公共类就可以只包装/装饰内部类,如下所示:
某些代码可能如下所示:
namespace MechanismProxy // Simulates Mechanism Proxy Assembly
{
public interface IMechanism
{
void DoStuff();
}
}
namespace MechanismImpl // Simulates Mechanism Assembly
{
using MechanismProxy;
// This class would be registered to IMechanism in the DI container
public class Mechanism : IMechanism
{
private readonly IMechanism _internalMechanism = new InternalMechanism();
public void DoStuff()
{
_internalMechanism.DoStuff();
}
}
internal class InternalMechanism : IMechanism
{
public void DoStuff()
{
// Do whatever
}
}
}
...当然,我仍然需要解决一些关于构造函数注入和将注入公共类的依赖项传递给内部类的问题。还有一个问题是外部程序集可能会更新公共机制......我需要一种方法来确保只有 DI 容器才能做到这一点......我想如果我能弄清楚,我什至不需要内部版本。无论如何,如果有人能帮助我了解如何克服这些架构问题,将不胜感激。
【问题讨论】:
-
解决这个问题的一种方法是让所有层都知道 DI 抽象。他们每个人都会公开他们的接口。如果您不希望其他程序集能够更新您的实现,它们会将它们保留在内部并通过 DI 抽象将它们添加到组合根。内核内置的 DI 就是一个很好的例子。
-
对我来说你太偏执了;o)
-
当您开始使用 AutoFac 或 Ninject 等实际 DI 实现时,这种完全不可知的 DI 的想法就会崩溃……它们都有一些独特的东西,如果您不让他们知道什么是 DI你正在使用一些基本的东西,在很多情况下都不起作用。如果您曾经尝试从 SQL db 切换到 MongoDB,这与存储库模式相同,它不会像宣传的那么简单。
-
@bubbleking 看看这篇介绍性文章msdn.microsoft.com/en-us/magazine/mt703433.aspx。
-
@bubbleking 我并不是真的建议放弃 DI,只是说没有一种正确的做事方式。这取决于你在做什么。例如,如果扭曲依赖于框架的东西,那么 DI 参考是更好的做事方式(类似于 prism 模块的东西)。如果您正在扭动 Nuget,您不希望依赖于其他事情,让用户决定何时使用 new 是有用的(他们可能正在使用一些奇怪的功能架构以及为什么强制 DI)。如果您不希望他们知道实现,则可以使用静态工厂方法。
标签: c# .net design-patterns dependency-injection architecture