依赖注入和循环引用

时间:2020-03-06 14:19:56  来源:igfitidea点击:

我刚刚开始进行DI和单元测试,但遇到了一些困难,对于那些更有经验的开发人员,我相信这是毫无道理的:

我有一个称为MessageManager的类,该类接收数据并将其保存到数据库中。在同一个程序集中(Visual Studio中的项目),我创建了一个存储库接口,其中包含访问数据库所需的所有方法。
此接口的具体实现在名为DataAccess的单独程序集中。

因此,DataAccess需要对MessageManager的项目引用来了解存储库接口。
而且MessageManager需要一个对DataAccess的项目引用,以便MessageManager的客户端可以注入存储库接口的具体实现。
这当然是不允许的

我可以将接口移到数据访问程序集中,但是我相信存储库接口应与使用该接口的客户端驻留在同一程序集中

那我做错了什么?

解决方案

我认为我们应该将存储库接口移至DataAccess程序集。然后,DataAccess不再需要引用MessageManager。

但是,由于我对体系结构几乎一无所知,因此仍然很难说...

我们只有两个选择:添加一个程序集以保存接口或者将接口移到DataAccess程序集中。即使我们正在开发一种体系结构,有朝一日有可能使用存储库接口的另一个实现者(甚至在另一个程序集中)替换DataAccess类,也没有理由将其从DataAccess程序集中排除。

我们应该将界面从任何一个组件中分离出来。将接口与使用者或者实现者放在一起会破坏具有接口的目的。

接口的目的是允许我们注入实现该接口的任何对象,无论它是否与DataAccess对象所属的程序集相同。另一方面,我们需要允许MessageManager使用该接口,而无需使用任何具体的实现。

将界面放在另一个项目中,问题就解决了。

通常,我们可以通过使用setter注入而不是构造函数注入来解决循环引用问题。

用伪代码:

Foo f = new Foo();
Bar b = new Bar();
f.setBar(b);
b.setFoo(f);

我们可以保留当前的结构(不存在导致问题的MessageManager到DataAccess的依赖关系),然后让MessageManager使用System.Reflection.Assembly类在运行时动态加载所需的具体实现。

我们是否正在使用控制容器倒置?如果是这样,答案很简单。

程序集A包含:

  • 消息管理器
  • 信息库
  • ContainerA(添加MessageManager)

程序集B包含(和ref的AssemblyA):

  • 存储库实现IRepository
  • ContainerB扩展了ContainerA(添加存储库)

程序集C(或者B)将启动应用程序/向MessageManager询问容器,该容器将知道如何解析MessageManager和IRepository。

依赖反转正在起作用:

高级模块不应依赖于低级模块。两者都应依赖抽象。抽象不应依赖细节。细节应取决于抽象。

DatAccess程序集中的类所依赖的抽象需要与DataAccess类以及该抽象的具体实现(MessageManager)放在单独的程序集中。

是的,那是更多的程序集。就我个人而言,这并不重要。我看不到多余的程序集有很大的缺点。