想象一下这些关系:

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

相反:

  • C有1个B
  • B有1A
  • 根据传递性,C 有 1 个 A

为了在数据库中建模这种关系,我们有:

TableA
a_id

TableB
b_id
a_id (fk to TableA)

TableC
c_id
b_id (fk to TableB)

为了在面向对象中建模这种关系,我们有:

objA
objB
objC

和...-OBJB提到OBJA -OBJC提到OBJB

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

选项1。

b.getA().runMethodX()

我认识的很多人都会这样做,但我也了解到这不好,因为 getA() 不是纯粹 OO 意义上的 B 行为。这样做就像进行过程编程一样。同意不同意?

选项 2。

通过构造函数注入/setter让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?

你又是什么?还有其他选择吗?评论?建议?

有帮助吗?

解决方案

选项 2 是相当危险的信息重复,此外,根据您的环境,参考图的形状可能会出现任何问题。

如果该方法在某种意义上依赖于 objA 的类型(作为一种双重调度),则选项 3 可能有意义。

选项 1 违反了得墨忒耳定律,但听起来它是干扰性最小的选项。

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

其他提示

从您的选择列表中,选择是选项 4,因为它遵循 德墨忒耳定律

  • 选项1违反了迪米特定律
  • 选项 2 引入了冗余引用 间接违反了迪米特法则
  • 选项 3 用对象 C 的知识污染了对象 A,这可以说也违反了德米特法则(相反)

剩下选项 4

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

我认为问题是你想要什么样的模型。如果您要使用提前了解整个结构且不会更改的关系模型,则选项 1 是可以接受的。即A中的数据/方法 在特定的组织模式中 是 C 所必需的。

选项 2 似乎是个好主意。但您本质上是保存对同一对象的两个引用。现在,如果 B 映射到不同的 A,B 也必须通知 C 更改其引用。不好。

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

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

您还可以隔离 A 上 C 所需的功能并为其创建一个接口。接口(而不是 A)将在构造时传递到 C。这将使 C 与 A 解耦。

不。1 或否。4.视情况而定。在我的金属切割软件中,我们有很多需要知道对象的父级/起源的实例。例如,我们在配件上有一个路径集合,并且每个路径都有一个配件属性,该属性返回创建它的配件。

但是,如果连接对象的存在仅仅是因为它可以帮助原始对象实现其目的(例如数学支持),那么委托可能是更好的方法。

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top