代码之家  ›  专栏  ›  技术社区  ›  Henry

OO中的一对多关系

  •  2
  • Henry  · 技术社区  · 16 年前

    想象一下这些关系:

    • 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)
    

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

    objA
    objB
    objC
    

    还有… -objb引用了obja -objc引用了objb

    如果objc有一个方法需要调用obja的方法,您会怎么做?

    选项1。

    b.getA().runMethodX()
    

    我认识的许多PPL都会这样做,但我也知道这样做不好,因为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 回复  |  直到 11 年前
        1
  •  2
  •   Jeffrey Hantin    16 年前

    选项2是一个相当危险的信息复制,除了参考图的形状可能产生的任何问题之外,还取决于您的环境。

    如果方法在某种意义上依赖于obja的类型,作为一种双重分派,选项3可能是有意义的。

    选项1违反了德米特定律,但听起来它是最不具侵入性的选项。

    您可能还需要考虑在objb上传递对obja的调用的转发方法。

        2
  •  0
  •   Steven A. Lowe    16 年前

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

    • 方案1违反了德米特定律
    • 选项2引入了冗余引用 间接违反德米特定律
    • 方案3用对象C的知识污染了对象A,这也违反了德米特定律(反过来说)。

    剩下选项4了

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

        3
  •  0
  •   lc.    16 年前

    我想问题是你想要什么样的模特。如果你要建立一个关系模型,在这个模型中你提前知道整个结构,并且它不会改变,那么选项1是可以接受的。也就是说,在 在特定的组织模式中 对于C是必需的。

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

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

        4
  •  0
  •   Bill.D    16 年前

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

    您还可以隔离C需要的函数,并为它创建一个接口。接口(而不是A)将在构造时传递到C中。这将使C和A脱钩。

        5
  •  0
  •   RS Conley    16 年前

    1号或4号视情况而定。在我的金属切割软件中,我们有许多实例需要了解对象的父对象/原点。例如,我们在配件上有一个路径集合,并且每个路径都有一个配件属性,该属性返回创建它的配件。

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

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

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

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