面向对象的2x一对多关系

时间:2009-02-19 02:30:58

标签: oop orm entity-relationship

想象一下这些关系:

  • 1 A有很多B
  • 1 B有很多C ......

相反:

  • C有1 B
  • B有1A
  • 通过传递性,C具有1 A

要在DB中建模这种关系,我们有:

TableA
a_id

TableB
b_id
a_id (fk to TableA)

TableC
c_id
b_id (fk to TableB)

要在OO中建模这种关系,我们有:

objA
objB
objC

和... - objB引用了objA - objC引用了objB

如果objC有一个需要调用objA方法的方法,你会怎么做?

选项1。

b.getA().runMethodX()

我知道很多人会这样做,但我也知道这不好,因为getA()不是纯粹OO意义上的B行为。这样做就像进行程序编程一样。同意/不同意?

选项2。

让objC通过构造函数注入/设置器直接引用objA和objB

这是个好主意吗?但是objC参考的objB也引用了objA。这个可以吗?或者只要它不是循环对象引用的情况,它是可以接受的吗?

选项3。

将有问题的方法移至objA,并通过参数传递objC。

我不确定这是否是一种选择。我认为它并不适用于所有情况。将objC中的方法减少到仅适用于其状态的最小值,并让objA执行objA之前或之后需要执行的操作。

选项4。(代表团)

将runMethodXinA()添加到B,调用

a.runMethodX()

C来电

b.runMethodXinA()

之前我尝试过这种方法,但B很可能最终拥有与A一样多的方法,并且B和A中没有1种方法违反了DRY?

你有什么打算?还有其他选择吗?评论?建议

5 个答案:

答案 0 :(得分:2)

选项2是一个相当危险的信息重复,除了根据您的环境可能来自参考图形状的任何问题。

如果方法在某种意义上取决于objA的类型,那么选项3可能是有意义的,作为一种双重调度。

选项1违反了得墨忒耳法,但听起来它是最不具侵入性的选择。

您可能还想在objB上考虑将调用传递给objA的转发方法。

答案 1 :(得分:0)

从您的选择列表中,选项是选项4,因为它服从Law of Demeter

  • 选项1违反了得墨忒耳法则
  • 选项2引入了冗余引用间接违反了Demeter法
  • 选项3污染具有对象C知识的对象A,这可能违反了得墨忒耳定律(反过来)

留下选项4

可能有选项5,但这超出了原始问题的范围; - )

答案 2 :(得分:0)

我认为问题是你想要什么样的模特。如果你想要一个关系模型,你提前知道整个结构并且它不会改变,那么选项1是可以接受的。也就是说,C中需要A 中特定组织模式中的数据/方法。

选项2似乎是一个好主意。但实际上你保存了对同一个对象的两个引用。现在,如果B被映射到不同的A,则B必须通知C也改变其引用。不好。

然而,选项4似乎是采用真正的OO方法的正确方法。关键是A可以改变它的结构,你只需要适应B - 直接的孩子。 C不知道也不关心A是如何实现的,只要A存在就足够了,B知道如何处理它。

答案 3 :(得分:0)

你真的需要从C到B到A的后向指针,或者你只有它们才能从C访问A?也许A应该负责创建B和C以及管理他们的关系。在这种情况下,A可以在创建C时将自身插入到C中(例如,C可能有一个带有A的构造函数)。

您也可以隔离C所需的A上的功能并为其创建接口。接口(而不是A)将在构造时传递给C.这将使C与A分离。

答案 4 :(得分:0)

没有。 1或4号视情况而定。在我的金属切割软件中,我们有许多实例需要知道对象的父/源。例如,我们在Fitting上有一个Path Collection,每个Path都有一个Fitting属性,返回创建它的拟合。

然而,如果连接对象只是因为它有助于原始对象实现其目的(如数学支持),那么委派可能是更好的方法。

如果连接的对象修改了原始对象,那么最好使用访问者模式。

唯一需要注意的是,在大多数OOP中,您必须小心双重链接的对象。对于具有相互连接的父子链接的对象,垃圾收集通常会失败。忘记清理父子链接是一个常见的错误。

使用支持创建和使用事件的OOP,您可以解决此问题。通过使用代理。父对象将向子进行代理对象而不是自身。当孩子需要父母时,它将从代理对象获得引用。代理对象反过来引发一个导致父进程返回的事件。没有硬链接,因此减轻了程序员或垃圾收集器未清理的问题。