依赖注入vs事件以及使用事件解决循环依赖性

Ric*_*ard 5 c# oop events dependency-injection

我一直在努力重构一个应用程序,以便通过依赖注入和所有好东西使其更易于管理.这样做时,我不止一次遇到过循环依赖.

所以这是循环依赖的典型示例:

interface IA
{
    int Data { get; }
}
interface IBefore
{
    void DoStuffBefore();
}
class A: IA
{
    public int Data { get; private set; }
    IBefore before;
    public A(IBefore before)
    {
        this.before = before;
    }
    public void Increment()
    {
        before.DoStuffBefore();
        Data++;
    }
}
class B: IBefore
{
    IA a;
    public B(IA a)
    {
        this.a = a;
    }
    public void WriteADataToConsole()
    {
        Console.Write(a.Data);
    }
    public void DoStuffBefore() //From IBefore
    {
        WriteADataToConsole();
    }
}
Run Code Online (Sandbox Code Playgroud)

我不能创建任何类,因为它们需要彼此.现在,在这种情况下要做的标准(?)事情是将A的数据与A分开:

public interface IA
{
    int Data { get; set; }
}
public interface IBefore
{
    void DoStuffBefore();
}
class AData : IA
{
    public int Data { get; set; }
}
class A
{
    public IA Data { get; private set; }
    IBefore before;
    public A(IA data, IBefore before)
    {
        this.Data = data;
        this.before = before;
    }
    public void Increment()
    {
        before.DoStuffBefore();
        Data.Data++;
    }
}
class B : IBefore
{
    IA a;
    public B(IA a)
    {
        this.a = a;
    }
    public void WriteADataToConsole()
    {
        Console.Write(a.Data);
    }
    public void DoStuffBefore() //From IBefore
    {
        WriteADataToConsole();
    }
}
Run Code Online (Sandbox Code Playgroud)

上面解决了循环依赖,因为我现在可以先创建AData,然后将其注入B并将B注入A.但我也可以放一个B可以监听的事件:

public interface IA
{
    int Data { get; }
    event Action BeforeEvent;
}

class A: IA
{
    public int Data { get; private set; }
    public event Action BeforeEvent;
    public void Increment()
    {
        BeforeEvent();
        Data++;
    }
}

class B
{
    IA a;
    public B(IA a)
    {
        this.a = a;
        a.BeforeEvent += new Action(WriteADataToConsole);
    }
    void WriteADataToConsole() //Event listener
    {
        Console.Write(a.Data);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我偶然发现的声音,因为我试图将事件方法转换为依赖注入,并意识到通过这样做我已经得到了循环依赖.

一些困扰我大脑的问题是:

  • 这两个解决方案都解决了循环依赖关系(对吗?),据我所知,它们可以将A扩展到相同的程度,但这被认为是最好的设计?
  • 关于何时使用事件以及何时使用DI来解决循环依赖性的一般指导原则是什么?
  • 显然,如果A需要来自B的返回值,事件就不好.这是否意味着当返回void时事件总是优先的?
  • 每种解决方案的优缺点是什么?

Aki*_*kim 4

好问题!在 95% 的情况下,您必须将这两个实体合并在一起或以其他方式打破依赖关系,但是\xe2\x80\xa6 如果由于某种原因或其他原因无法将它们合并到一个实体中怎么办(使用 UI有时可能会很棘手)?Mark Seemann写了一本关于“.NET 中的依赖注入”的书,其中描述了两种打破循环依赖的方法:

\n\n
    \n
  • 根据 DI 书籍,事件\xe2\x80\x94 是更好的方式,并且您已经这样做了。对我来说看起来不错
  • \n
  • 属性注入\xe2\x80\x94 与构造函数注入相反,属性注入意味着注入的资源是可选的
  • \n
\n\n

在带有属性的第二个实现中,有一个构造函数:public A(IA data, IBefore before)\n。和IA data都是依赖注入方面所IBefore before必需\xe2\x80\x94 这里是打破 cicle 的最佳点!这是一个可选的实现IBefore

\n\n
class A\n{\n    public IA Data { get; private set; }\n    public IBefore Before { get; set; }\n\n    public A(IA data)\n    {\n        this.Data = data;\n    }\n    public void Increment()\n    {\n        // here should be design decision: if Before is optional\xe2\x80\xa6\n        if(Before == null)\n        {\n            Before.DoStuffBefore();\n        }    \n\n        // \xe2\x80\xa6or required\n        if(Before == null)\n        {\n            throw new Exception("\'Before\' is required");\n        }\n\n        Data.Data++;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

由您决定,如果是可选的,则跳过Before.DoStuffBefore()调用Before,或者在需要时引发异常

\n\n

根据你的问题:

\n\n
    \n
  • 哪一个被认为是最好的设计?有哪些指导方针?优点和缺点\xe2\x80\x94 恕我直言,都可以。事件比较笼统。属性更容易实现和处理
  • \n
  • 当返回 void 时,事件总是更好?\xe2\x80\x94 对我来说是的
  • \n
\n