4

我一直在这里阅读有关解决软件设计中的循环依赖的不同答案,但我仍然无法理解。请帮助我理解它。

假设我有 A 类和 B 类,它们相互调用方法:

class A {
    public:
        void Do1() {
            B b;
            b.Do1();
        }

        void Do2() {
            //do something
        }
};

class B {
    public:
        void Do1() {
            //do something
        }

        void Do2() {
            A a;
            a.Do2();
        }
};

类根据单一职责原则完美划分。

看起来我不能在这里应用依赖倒置原则,因为我只能将一个类聚合到另一个类中。

根据最佳设计实践,你能建议我如何处理这个问题的正确方法吗?

我会很感激你用一些代码回答。

4

2 回答 2

4

您需要通过将它们放入另一个类来删除循环依赖。从高层次来看,循环依赖是两个类可以相互交互的众多方式之一。正确的做法是使它们彼此松散耦合,使它们远离显式交互,这使您可以独立地改变类。请查看调解员模式。在以下示例中,C 类是一个逻辑中介。

class A {
    public:
        void Do2() {
            //do something
        }
};

class B {
    public:
        void Do1() {
            //do something
        }

};   


 class C
    {
      private A a;
      private B b;
      C(A a, B b)
      {
         this.a = a; this.b = b;
      }

      public void A_do1 { b.do1(); }
      public void B_do2 { a.do2(); }
    }
于 2015-03-23T11:36:45.837 回答
2

让我们从维基百科给出的循环依赖概述开始

循环依赖在许多域模型中是很自然的,其中同一域的某些对象相互依赖。然而,在软件设计中,较大的软件模块之间的循环依赖被认为是一种反模式,因为它们的负面影响......

让我们打破它:

  1. 循环依赖在许多域模型中是很自然的,其中同一域的某些对象相互依赖。

它说自然。以下是来自 的示例Ado.NetDataTableDataRow对象

DataTable t = new DataTable("MyNewTable");
DataRow r = t.NewRow();
t.Rows.Add(r);
// Lets inspect it
Debug.WriteLine(r.Table.TableName);
// This will print - MyNewTable

在这个例子中,你有一个 CD 所在的子系统,正如他们所说 - 自然。例如,您知道汽车的每个主要部件上都有 VIN 号。让我们用伪代码实现它

class Car{

    string Vin { get; set; }
    void AddDoor(Door.Position pos){
        Door d = new Door();
        d.DoorPosition = pos;
        d.Car = this; // notice this - door knows about a car!!
        _doors.Add(d);
    }

}
class Door{
    Car CarAttachedTo;
    Position DoorPosition;

    public enum Position {
        DriverFront
        DriverRear
        PassangerFront
        PassangerRear
   } 
}

让我们想象一下,有人把你的门关掉了,警察把它找回来了,你需要确定它是你的门还是别人的 - 然后你做:

if (myDoor.CarAttachedTo.Vin == "MY_CAR_VIN_NUMBER")
    MessageBox.Show("The door is stolen off my car"); 

理念:在紧密协作和组织在一起的子系统中,CD 有时还可以

  1. 较大的软件模块之间的循环依赖被认为是一种反模式

您的应用程序可以在物理上位于同一个程序集(dll、exe)中,而在逻辑上它可以具有单独的逻辑层。例如,您可以在同一个程序集中编写您的 UI、BLL 和 IO。因此很容易在你的 UI 和 BLL 之间创建循环依赖。如果每一层都存在于一个单独的程序集中,这将更加困难。

CD 经常与紧耦合相混淆。虽然在上面的示例中我们看到了 CD 有时是多么有用,但紧密耦合通常意味着一个具体对象知道另一个具体对象,并且没有可重用性、可扩展性和可测试性的空间。让我们回到车上。让我们详细了解汽车上的吊门 - 你的汽车经过装配线,它来到了门贴机

class DoorHandler{

    private Car _car;
    private MitsubishiDoorAlignmentMachine _doorMachine;        

    void HangDoors(){
        foreach (Door d in _car.Doors){
            // hang the door using 
            _doorMachine.Hang(d)
        }
    }

}    

但是现在我们遇到了一个问题——我们的 Door 处理程序只适用于MitsubishiDoorAlignmentMachine. 如果我想用 替换它FujitomoHeavyIndustriesDoorAlignmentMachine怎么办?然后我需要做这样的事情:

class DoorHandler{

    private Car _car;
    private IDoorAlignmentMachine _doorMachine;        

    void HangDoors(){
        foreach (Door d in _car.Doors){
            // hang the door using 
            _doorMachine.Hang(d)
        }
    }

}

// And when I crate my DoorHandler - I tell it which machine to use
new DoorHandler(new FujitomoHeavyIndustriesDoorAlignmentMachine());

这种方式我DoorHandler不依赖于任何特定的机器

现在,让我们回到您的情况 -AB. 可能有多个答案。如果您正在创建需要在对象之间进行交互但可能有多个实现的子系统 - 只需使用接口。这样,您AB知道签名而不是具体对象。如果只有一个实现并且对象是同一个程序集中的同一个子系统的一部分 - 相互了解可能就可以了。

于 2015-03-23T00:25:31.453 回答