【问题标题】:Java - 2 Way 'Has A' RelationshipJava - 2 Way '有 A' 关系
【发布时间】:2013-08-11 12:34:36
【问题描述】:

我刚刚启动了一个项目,旨在让我的雇主成为一款管理软件。我有一个琐碎但可能很简单的查询,我似乎找不到任何信息。

在对象之间有 2 种方式“有”关系是谨慎/良好的做法吗?例如,Client 对象“有一个”Site,然后Site“有一个”Client,其中Client 对象是“有”@ 的Client 987654327@?

public class Client {
    Site site;
}

public class Site {
    Client client;
}

对此有什么不妥(没有双关语)吗?我目前正在为该项目创建一个模型 UML,这一直困扰着我。

【问题讨论】:

  • 好问题,这也一直困扰着我。
  • 完全正常。想想父母/孩子的关系。有时孩子有必要接触父母,反之亦然。
  • 请记住,如果您不小心,可能会遇到数据完整性问题:bob.site = a; a.client = joe;
  • @damo 应该没问题。我将在这个项目上投入大量精力来实现松散耦合,仅仅是因为它的复杂性。
  • 中介者设计模式。前面说这会弄乱你的头。 有时,就在关于中介者模式的混乱旁边是一个解决方案,如果你遵循它完全将帮助你使用这两个对象需要个人堆栈跟踪。

标签: java oop object object-oriented-analysis


【解决方案1】:

这有什么不妥,还是可以的?

对此没有明确的答案。最佳答案是:这取决于您的应用程序的设计。

何时使用

如果您的Client 对象应该导航到Site 对象并且您的Site 对象应该导航到Client 对象,那么您代码中的当前示例就可以了。不过,您可能仍需要某种方式来关联这些元素,可能是通过其中一个类或两者中的附加 id 字段。

如果您使用的框架可以帮助您自动绑定类,如 Hibernate,那么维护循环引用对您来说不是问题。

什么时候不使用它

基本上,对于文本序列化,因为它会产生一个无限循环。正如Raibaz's answer 中已经提到的,像Jackson 这样的库在将ClientSite 类序列化为JSON 字符串时会陷入无限循环1。请注意,这在序列化到其他 String 数据时也有效,例如通过 XML 中的 JAX-WS Web 服务传递对象(更多信息:What happens to generic class in jax-ws webservice?)。

1 这可以使用属于特定库的注释 (@Something) 来解决,例如@SimonAndréForsberg 指出,@SimonAndréForsberg 指出,@JsonManagedReference@JsonBackReference 来自 Jackson library,但此解决方案的缺点是您的类将与库紧密耦合。

【讨论】:

  • 有了一个以某种方式更新的 Jackson 版本,有很多方法可以避免在这种情况下出现无限循环,要么使用 @JsonManagedReference@JsonBackReference 结合使用,要么使用可爱的 @JsonIdentityInfo .我最近经常使用这些解决方案,它们与 Jackson 完美配合。
  • @SimonAndréForsberg 这种方法的缺点是您的类与 Jackson 紧密耦合。编程生活中并非所有美好的事物都是免费的 =\
  • 当然,你说得有道理。但是由于杰克逊太棒了,我并不害怕将我的课程与它结合起来:)(我知道,我可能会后悔这么说)
  • @SimonAndréForsberg 这是一个设计决策,将取决于每个项目。就我而言,我宁愿尽可能避免使用它们,至少证明我可以像 Spring 和 EJB/CDI 注释一样轻松替换它们。
  • 我不确定 Spring+EJB/CDI 注释是什么(请赐教!我使用过 Spring,但不确定您在说什么)。但是,我刚刚发现在杰克逊可以decouple the classes from Jackson entirely using mix-in annotations。解耦解决了:)
【解决方案2】:

这很常见,但我会考虑您希望子对象与其父对象的耦合松散程度。如果您有子对象对父对象的引用,那么您将无法与另一个父对象/没有父对象重用该对象。

【讨论】:

    【解决方案3】:

    对象之间存在相互关联是很常见的。

    例如,在某些用户界面工具包中,可视组件将引用其子组件,并且每个子组件都可能引用其父组件。

    has-a 一词经常用于传达一个对象对另一个对象的所有权。当这是真的时,这种关系通常是单向的。

    Booch、Rumbaugh 和 Jacobson 的“统一建模语言用户指南”中提供了这些术语的一个很好的定义:

    聚合——两个类之间的简单关联表示对等点之间的结构关系,这意味着两个类在概念上处于同一级别,没有一个比另一个更重要。有时,您会想要对“整体/部分”关系建模,其中一个类代表一个较大的事物(“整体”),它由较小的事物(“部分”)组成。这种关系称为聚合,代表一种“有”的关系,即整体的一个对象有部分的对象。

    【讨论】:

      【解决方案4】:

      像您的示例中那样具有双向关联是很正常且很常见的。

      您可能要考虑的唯一警告是,当您要序列化对象时,例如在使用 Jackson 或任何其他库的 json 中,您应该从序列化中排除关联的两侧之一,以避免以无限循环。

      【讨论】:

      • 是的,我没有想到这一点。很高兴有潜在的陷阱引起我的注意。 :)
      • @RudiKershaw 一些序列化框架和 API 对此有解决方法,例如使用标识符而不是每次都序列化整个对象图。
      • @LuiggiMendoza 对。我正在考虑类似杰克逊的@JsonIdentityInfo 之类的东西,它在序列化过程中删除了无限引用循环。
      • @SotiriosDelimanolis 在我的最后一个示例中,我还试图说:即使框架为您处理,您并不总是需要序列化所有对象字段(在下拉列表的情况下,您只需要 2 个字段,仅此而已),但这超出了本问题的范围。
      • 使用杰克逊,avoid the infinite loop 和避免extra coupling with Jackson 都是完全可能的,这就是为什么我不赞成这个答案。
      【解决方案5】:

      我会尽可能坚持单向关系。 2-way 关系将两个类联系在一起,其中一个类的更改会导致另一个类更改两个。问问自己这种关系是否真的很自然......

      如果必须,请确保两者都是接口或至少其中一个是接口。

      【讨论】:

      • “其中一个发生变化会导致另一个也发生变化”?如何?为什么?你能举个例子吗?我不得不在这里不同意你的看法。双向关系是完全正常的 IMO(考虑一个 XML 树结构,例如,父母知道他们的孩子,孩子知道他们的父母)。
      • XML 与 Java 无关。如果两个对象都相互引用,这通常意味着它们广泛使用彼此的接口。这意味着其中一个的更改也会导致需要更改另一个。当然,您始终可以通过使此界面至少稳定来防止它发生。
      • XML 树结构就是一个例子。有许多结构将孩子和父母联系在一起。我仍然不理解(或同意)“其中一个的改变导致需要改变另一个”。您是在谈论接口本身吗?在那种情况下,我认为这与问题无关。您是在谈论这些对象中的对象引用吗?在那种情况下,我认为通常当您使用这种 2 向关系时,您不会只更改其中一个这样的连接。
      • 看起来您已经在上一份声明中回答了自己的问题。让我引用:“当你使用这种双向关系时,你不会只改变其中一个这样的连接”——这就是我的意思。在创建此类构造时,您应该牢记这一点。但是,是的,这是完全正常的,让我说清楚。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-15
      • 2016-07-09
      • 2021-11-02
      • 1970-01-01
      • 1970-01-01
      • 2011-02-19
      • 2022-12-02
      相关资源
      最近更新 更多