我一直在阅读有关解决软件设计中循环依赖的不同答案,但我仍然无法得到它。请帮我理解。
假设我有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();
}
};
根据单一责任原则完全划分课程。
我似乎无法在此处应用依赖性倒置原则,因为我只能将聚合一个类转换为另一个类。
根据最佳设计实践,您能否告诉我如何处理这个问题?
我很感激您使用某些代码获得答案。
答案 0 :(得分:3)
您需要通过将循环依赖项放入另一个类来删除它。从高层次来看,循环依赖是两个类可以相互交互的众多方式之一。正确的方法是让它们通过明确地彼此交互而使它们彼此松散地耦合,这让你可以独立地改变类。请检查调解员模式。在以下示例中,类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(); }
}
答案 1 :(得分:2)
让我们从维基百科提供的overview of circular dependency开始
在许多域模型中,循环依赖是很自然的,其中同一域的某些对象彼此依赖。然而,在软件设计中,较大的软件模块之间的循环依赖性被认为是反模式,因为它们的负面影响......
让我们打破它:
- 在许多域模型中,循环依赖是很自然的,其中同一域的某些对象相互依赖。
醇>
它说自然。以下是Ado.Net
的示例。 DataTable
和DataRow
个对象
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有时可以正常
- 较大软件模块之间的循环依赖关系被视为反模式
醇>
您的应用程序可以物理上位于同一个程序集(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)
}
}
}
但现在我们遇到了问题 - 我们的门处理程序仅适用于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
不依赖于任何特定的机器
现在,让我们回到您的情况 - A
和B
。可能有多个答案。如果您正在创建需要在对象之间进行交互的子系统,但可能有多个实现 - 只需使用接口。这样,您的A
和B
将知道签名而不是具体对象。如果只有一个实现,并且对象是同一个程序集中同一个子系统的一部分 - 相互了解就可以了。