解决模块化体系结构中的前向引用
本文关键字:引用 模块化 体系结构 解决 | 更新日期: 2023-09-27 18:26:13
在我的(C#)软件项目中,我有几个模块(由单独的Visual Studio项目表示),由于历史原因,它们有很多循环依赖关系(紧密耦合)。
我目前正在尝试解析/删除循环引用。我已经添加了依赖项注入,但这当然不能解决/删除循环引用。
因此,我考虑使用事件来解决"正向"依赖关系。它们不直接调用前向依赖项,而是在该事件上注册。
然而,这些依赖关系中的一些需要在其他依赖关系之前执行,例如:
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
的所有类都依赖于当前类;所以这些是我想要解决的循环引用。
是否有任何现有的模式可以解决这种"复杂事件"机制?或者还有其他更好的方法来解决这个问题吗?
我试过用谷歌搜索这个问题,但找不到任何好的搜索关键字。
我认为使用事件来改进紧密耦合的设计不是最好的选择,因为它们往往会使依赖关系不那么明显。而且,您确实需要显式地处理所有依赖项来处理循环引用。
我建议您逐步应用依赖反转原则和界面分离原则。通过构造函数将新引入的接口传递给它们的客户端,并使用DI来组合它们
此外,依赖关系可视化工具在这些情况下确实很有帮助。我过去曾使用Resharper和NDepend来完成这些任务,两者都运行良好。
DIP示例
下面是一个示例,展示了如何使用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-3,必须在CompanyDeleted
事件上按顺序运行,并且您自己的事件处理程序应该最后运行。
这是一种脆弱的做法。让我们假设依赖项的名称如下:
- 消息管理器
- 项目经理
- UserManager
其动机是消息管理器必须首先运行,这样它才能从用户管理器中找到所有用户,从项目管理器中查找所有项目,从而能够删除其消息。
相反,你可以颠倒这个过程。监听事件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);
}
}
}
希望能有所帮助。