我在这里已经阅读了有关循环引用的其他问题,但我无法找到问题的答案。
我有三个类库:Authentication,EmailService和ExceptionService。
身份验证控制用户登录各种应用程序,EmailService发送电子邮件,ExceptionService将错误/异常记录到数据库。
目前,身份验证引用了EmailService和ExceptionService,以使用它们的功能,这是有效的。 ExceptionService引用EmailService来发送报告电子邮件。一切都很好。
我想知道的是,以下是否可行/可取/愚蠢,以及是否有更好的方法:
我希望EmailService能够使用ExceptionService的功能,因此会报告EmailService中的任何错误。从理论上讲,这可能意味着ExcpetionService会回调EmailService发送报告电子邮件,这可能会触发相同的错误,因此我必须编写一个仅由EmailService使用的方法,该方法不会发送电子邮件,只记录它。
ExceptionService仍应引用EmailService。
Authentication类库还应该使用其他两种服务。
这听起来非常复杂和圆形,这就是为什么我认为这可能不是一件好事。但我该怎么做呢?
我已尝试在EmailService中引用ExceptionService,但在创建私有ExceptionService对象并尝试使用它时,它将无法编译。
我想我真正想要的是我的任何应用程序都引用了EmailService和ExceptionService,但是他们也互相引用。
到目前为止,解决此问题的唯一方法是忘记在EmailService中报告异常。
非常感谢你的帮助:)。
答案 0 :(得分:2)
您遇到问题的原因是因为您正在紧密耦合您的类,并且当您尝试创建循环耦合时,编译器会非常明智地感到不安。您可以通过为服务创建接口并将一个服务的接口实例提供给另一个服务的实现来解决此问题,反之亦然。
更好的解决方案是停止重新发明轮子并使用现有的日志记录框架。 NLog和Log4Net都可以满足您的日志记录和电子邮件需求。
答案 1 :(得分:1)
合并程序集。将项目拆分为尽可能多的程序集是一个常见错误。这增加了管理负担并且在循环引用的情况下引起麻烦。应避免紧耦合,但不能完全避免。接受它。
您的情况是循环引用的有效情况。由于逻辑原因,这两个类只需要彼此。
您可以使用接口解决循环引用问题,但依赖关系仍然存在于运行时。这些接口不会提高代码质量,只会关闭编译器警告。
不管理程序集的依赖项。使用命名空间和文件夹。程序集是部署单元,而不是依赖关系管理工具。
答案 2 :(得分:1)
问题是你使用的是具体的类,而不是接口。因此,我的主张是引入两个接口,即: IExceptionService , IEmailService ,并将它们放在单独的项目中,例如: Services 。包含这两个服务的实现的项目将引用此新项目。由于 ExceptionService 可以使用 IEmailService 而 EmailService 可以使用 IExceptionService 。同时, ExceptionService 和 EmailService 可以在不同的程序集中定义。
什么是重要的 ExceptionService 和 EmailService 我们不知道这些接口背后的内容。具体实现应该以某种方式注入。为此,您可以使用依赖注入容器。如果您不想使用其他新库,还可以实现简单的服务定位器。
答案 3 :(得分:-1)
(我现在忽略了AuthenticationService,因为它只会混淆问题 - 你所拥有的是两个服务之间的简单循环依赖 - 例外和电子邮件)。
解决这些循环依赖问题的最佳方法是使用一层接口和一个存储库。
假设您有两个班级EmailService
和ExceptionService
。他们不能引用彼此的DLL,所以你要做的是,创建第三个程序集Interfaces
,并为它们创建两个接口,IEmailService
和IExceptionService
。现在,您的两个类都只能引用共享的接口程序集。
使用某种Inversion of Control机制,您的EmailService
会获得IExceptionService
的引用,反之亦然,因此圈子会被破坏。
一个简单的IoC机制是服务定位器模式。创建此(简化)对象:
public class ServiceLocator
{
public static IEmailService EmailService {get;set;}
public static IExceptionService ExceptionService {get;set;}
}
现在,您的EmailService在启动时可以使用ServiceLocator注册自己,并允许其他类获取对它的引用,而不依赖于它的程序集。
当然,大多数IoC解决方案都比它更多,但这是基本的想法 - 通过将共享接口提取到共享程序集中来防止循环依赖,并仅引用它。