在我的(C#)软件项目中,我有几个模块(由不同的Visual Studio项目表示) - 由于历史原因 - 有很多循环依赖(紧耦合)。
我目前正在尝试解决/删除循环引用。我已经添加了依赖注入 - 当然,这并不能解决/删除循环引用。
所以,我正在考虑使用事件来解决"转发"依赖。他们不会直接调用前向依赖,而是会在此事件上注册。
但是,其中一些依赖项需要在其他依赖项之前执行,例如这样(dumbed down)示例:
void DeleteCompany(int companyId)
{
// must run before other dependencies
ForwardDepend1.OnCompanyDeleted(companyId);
// can run in parallel
ForwardDepend2.OnCompanyDeleted(companyId);
ForwardDepend3.OnCompanyDeleted(companyId);
// must run as last command
this.OnCompanyDeleted(companyId);
}
ForwardDependX
的所有类都取决于当前的类;所以这些是我想要解决的循环引用。
是否有任何现有的模式可以解决这种复杂的事件"机制?或者是否有其他更好的方法可以解决这个问题?
我尝试使用谷歌搜索这个问题,但我找不到任何好的搜索关键字。
答案 0 :(得分:0)
我认为使用事件来改善紧密耦合设计并不是最好的选择,因为它们往往会使依赖性变得不那么明显。并且您确实需要明确所有依赖项来处理循环引用。
我建议您逐步应用dependency inversion principle和interface segregation principle。通过构造函数将新引入的接口传递给客户端,并使用DI来组合它们。
此外,依赖可视化工具确实有助于这些情况。我过去曾使用Resharper和NDepend来完成这些任务,两者都运行良好。
这是一个示例,说明如何使用DIP解决循环依赖关系。所以假设你有两个相互依赖的类:
class A
{
private readonly B _b;
public A(B b) {
_b = b;
}
public void DoIt() {
// ...
}
}
class B
{
public void DoSomething(A a) {
a.DoIt();
}
}
这意味着我们有一个像这样的依赖图:
A <--> B
现在在类中引入一个构成较弱依赖关系的接口,在本例中为A
(因为A
仅在B
暂时使用)。
interface IA
{
void DoIt();
}
class A : IA
{
private readonly B _b;
public A(B b) {
_b = b;
}
public void DoIt() {
// ...
}
}
class B
{
public void DoSomething(IA a) {
a.DoIt();
}
}
随着界面的引入,依赖关系圈已被破坏:
A ----
| |
v v
IA <-- B
答案 1 :(得分:0)
那不会解决你的问题。相反,你应该创建多个事件并将它们连接起来。
你说你有依赖关系1-3必须在CompanyDeleted
事件上按顺序运行,并且你自己的事件处理程序应该最后运行。
这是一种脆弱的方法。我们假设依赖项的名称如下:
动机是消息管理器必须首先运行,以便它可以从用户管理器中找到所有用户,并从项目管理器中找到所有项目,以便能够删除它的消息。
您可以改为反转该过程。聆听事件UserDeleted
以删除用户的所有消息,并删除ProjectDeleted
甚至删除项目的所有消息。
所以现在你在CompanyManager
的事件处理程序之间没有时间耦合,因为你只有一个事件处理程序。
它当然意味着更多的工作,因为你必须识别新事件并创建它们。好处是您可以在IoC容器的帮助下解决事件订阅。
创建一个名为IEventHandler<T>
的新界面:
public interface IEventHandler<T>
{
void Handle(T evt);
}
让你的类实现它:
public class UserManager : IEventHandler<CustomerDeleted>
{
public UserManager(IUserRepository repos, IEventPublisher publisher)
{
}
public void Handle(CustomerDeleted evt)
{
var users = _repos.FindUsersForCustomer(evt.CustomerId);
foreach (var user in users)
{
_repos.Delete(user);
_publisher.Publish(new UserDeleted(user.Id, user.CustomerId));
}
}
}
事件发布者本身使用您的容器(用于服务位置)。实施因容器而异:
public class EventPublisher : IEventPublisher
{
public EventPublisher(IYourContainerInterface container)
{
}
public void Publish<TEvent>(TEvent evt)
{
var handlers = _container.ResolveAll<TEvent>();
foreach (var handler in handlers)
{
handler.Publish(evt);
}
}
}
希望有所帮助。