我刚刚开始使用DI&单元测试并遇到了障碍,我确信这对于那些经验丰富的开发人员来说并不是一件好事:
我有一个名为MessageManager的类,它接收数据并将其保存到数据库。在同一个程序集(Visual Studio中的项目)中,我创建了一个存储库接口,其中包含访问数据库所需的所有方法。 此接口的具体实现位于名为DataAccess的单独程序集中。
因此,DataAccess需要对MessageManager的项目引用才能了解存储库接口。 MessageManager需要对DataAccess的项目引用,以便MessageManager的客户端可以注入存储库接口的具体实现。 这是不允许的,
我可以将界面移动到数据访问程序集中,但我相信存储库界面与使用它的客户端位于同一个程序集中
那我做错了什么?
答案 0 :(得分:3)
您应该将接口从任一组件中分离出来。将接口与消费者或实现者一起放置使接口失效的目的。
接口的目的是允许您注入实现该接口的任何对象,无论它是否与DataAccess对象所属的组件相同。另一方面,您需要允许MessageManager使用该接口,而无需使用任何具体实现。
将您的界面放在另一个项目中,问题就解决了。
答案 1 :(得分:2)
您只有两个选择:添加程序集以保存接口或将接口移动到DataAccess程序集中。即使您正在开发一种架构,其中DataAccess类有一天可能会被存储库接口的另一个实现者(甚至在另一个程序集中)替换,但没有理由将其从DataAccess程序集中排除。
答案 2 :(得分:2)
您使用的是反转控制容器吗?如果是这样,答案很简单。
程序集C(或B)将启动应用程序/向容器询问MessageManager,它将知道如何解析MessageManager 和 IRepository。
答案 3 :(得分:1)
我认为您应该将存储库接口移动到DataAccess程序集。然后DataAccess不再需要引用MessageManager。
但是,由于我对你的架构几乎一无所知,所以很难说...
答案 4 :(得分:1)
通常可以使用setter注入而不是构造函数注入来解决循环引用问题。
在伪代码中:
Foo f = new Foo();
Bar b = new Bar();
f.setBar(b);
b.setFoo(f);
答案 5 :(得分:0)
Dependency inversion正在发挥作用:
高级模块不应该依赖于低级模块。两者都应该依赖于抽象。抽象不应该依赖于细节。细节应取决于抽象。
DatAccess程序集中的类所依赖的抽象需要与DataAccess类和该abstration(MessageManager)的具体实现位于一个单独的程序集中。
是的,这是更多的装配。就个人而言,这对我来说并不是什么大不了的事。我没有看到额外装配的重大缺点。
答案 6 :(得分:-1)
你可以保留当前拥有它的结构(没有从MessageManager到DataAccess的依赖导致问题)然后让MessageManager
使用System.Reflection.Assembly
类动态加载运行时所需的具体实现