如何解决OOP中的交叉引用?

时间:2009-03-05 06:45:31

标签: oop

我现在遇到过这种情况,我想知道解决循环引用的OO方法是什么。我的意思是,A类将B类作为成员,B又将A类作为成员。

这方面的一个例子是将Person配偶作为成员的类Person。

Person jack = new Person("Jack");
Person jill = new Person("Jill");
jack.setSpouse(jill);
jill.setSpouse(jack);

另一个例子是将某些其他产品的集合作为成员的产品类。该集合可以是例如对该产品感兴趣的人可能也感兴趣的产品,并且我们希望在每个产品基础上维护该列表,而不是在相同的共享属性上(例如,我们不希望仅显示)所有其他产品属于同一类别。)

Product pc = new Product("pc");
Product monitor = new Product("monitor");
Product tv = new Product("tv");
pc.setSeeAlso({monitor, tv});
monitor.setSeeAlso({pc});
tv.setSeeAlso(null);

(这些产品只是为了说明问题,问题不在于某些产品是否会相互关联)

一般来说,这在OOP中是不是很糟糕?所有OOP语言是否/应该允许这样做,还是只是不好的做法?如果这是不好的做法,解决这个问题最好的方法是什么?

7 个答案:

答案 0 :(得分:2)

您提供的示例(无论如何)是合理的OO设计的例子。

您描述的交叉引用问题不是任何设计过程的工件,而是您作为对象表示的事物的真实特征,因此我没有看到存在问题。

你遇到了什么让你觉得这种方法设计不好?

3月11日更新:

在缺乏垃圾收集的系统中,显式管理内存管理,一种常见的方法是要求所有对象都有所有者 - 其他一些对象负责管理该对象的生命周期。

一个例子是Delphi的TComponent类,它提供级联支持 - 销毁父组件,并且所有拥有的组件也被销毁。

如果您正在使用这样的系统,那么中描述的参考循环类型可能会被认为是糟糕的设计,因为没有明确的所有者,没有一个对象负责管理生命周期。

我在某些系统中看到这种处理的方式是保留引用(因为它们正确捕获业务问题),并添加一个显式 TransactionContext 对象,该对象拥有加载到的所有内容来自数据库的业务域。此上下文对象负责了解需要保存哪些对象,并在处理完成时清除所有对象。

答案 1 :(得分:1)

这个问题的主要时间是,如果它变得太混乱,无法应对或维护,因为它可能成为一种意大利面条代码。

然而,要触及你的例子;

另请参阅完全有效如果这是您的代码中需要的功能 - 它是一个简单的指针(或参考)列表,用户可能感兴趣的其他项目。

类似地,添加配偶是完全有效的,因为这是一个简单的真实世界关系,不会让维护代码的人感到困惑。

我一直把它视为潜在的代码气味,或者可能是一个警告,要退后一步并使我正在做的事情合理化。

对于某些在代码中发现递归关系的系统(在上面的评论中提到),无论这种设计如何,都可以出现这些关系。我最近研究过一种元数据捕获系统,它具有递归的“类型”关系 - 即列与其他列在逻辑上相关。它需要由试图解析系统的代码处理。

答案 2 :(得分:1)

这不是OO设计中的根本问题。它可能成为问题的时间示例是图遍历,例如,找到两个对象之间的最短路径 - 您可能会进入无限循环。但是,这是你必须根据具体情况考虑的事情。如果您知道在这种情况下可能存在交叉引用,则编写一些检查以避免无限循环(例如,维护一组访问节点以避免重新访问)。但是如果没有理由它可能是一个问题(比如你在问题中给出的例子),那么拥有这样的交叉引用并不坏。在许多情况下,正如您所描述的那样,这是解决当前问题的一个很好的解决方案。

答案 3 :(得分:1)

我不认为这是交叉引用的一个例子。

交叉引用通常与这种情况有关:

class A
{
    public void MethodA(B objectB)
    {
       objectB.SomeMethodInB();
    }
}

class B
{
    public void MethodB(A objectA)
    {
        objectA.SomeMethodInA();
    }
}

在这种情况下,每个对象彼此“相互接触”; A呼叫B,B呼叫A,它们紧密耦合。如果A和B位于不同的包/名称空间/程序集中,则会更糟;在许多情况下,这些会在线性编译程序集时产生编译时错误。

解决这个问题的方法是让任何一个对象使用所需的方法实现一个接口。

在你的情况下,你只有一个级别的“伸手可及”:

public Class Person
{
    public void setSpouse(Person person)
    { ... }
}

我不认为这是不合理的,甚至也不是交叉引用/循环引用的情况。

答案 4 :(得分:0)

我不认为循环引用是一个问题。

但是,将所有这些关系放在对象中可能会增加太多混乱,因此您可能希望在外部表示它们。例如。您可以使用哈希表来存储产品之间的关系。

答案 5 :(得分:0)

引用其他对象根本不是一个真正糟糕的OO设计。这是在每个对象中管理状态的方式。

一个好的经验法则是得墨忒耳法则。看看这篇完美的LoD论文(Paperboy和钱包):click here

答案 6 :(得分:-2)

解决此问题的一种方法是通过id引用其他对象。

e.g。

Person jack = new Person(new PersonId("Jack"));
Person jill = new Person(new PersonId("Jill"));
jack.setSpouse(jill.getId());
jill.setSpouse(jack.getId());

我不是说这是一个完美的解决方案,但它会阻止循环引用。您正在使用对象而不是对象引用来建模关系。