【问题标题】:Creating interdependent objects创建相互依赖的对象
【发布时间】:2010-08-07 17:34:52
【问题描述】:

我有一个设计问题,我找不到干净又好的解决方案。我正在使用 PHP 进行开发,但我相信这可能会出现在任何语言中。我的基本问题是我有两个在某种间接级别上具有循环相互依赖关系的对象。这意味着我有一个实现 Facade 模式的类(称为 F),它包含一个(B 类的)对象,该对象本身需要创建 A 类的对象。类 A 的构造函数本身需要创建一个外观 F => 我有对象的循环相互依赖。

我相信我无法解决循环相互依赖(对象基本上实现了一个有限状态机,并使用状态模式进行循环),所以我正在寻找一个干净的解决方案。我自己提出了两种可能的解决方案,但我认为任何一种都不是特别优雅:

  1. 让类 A 实现一个 setFacade(F $facace) 方法并从构造函数中移除整个外观,并在创建 A 和外观之后设置它。 A 类的对象在没有外观的情况下无法工作,因此这实际上会创建一个 A 类的对象,该对象在调用 setFacade 之前无法执行任何操作,它会减少封装并允许在对象运行时替换外观,我也没有'不喜欢。

  2. 实现类似 Promise 的东西,它传递给 A 而不是外观,它稍后将能够在创建外观后立即解析它。我不喜欢介绍这个额外的间接层,特别是因为我没有比在 A 内部处理业务逻辑的方法中真正解决承诺的好地方,这可能 a) 产生可怕的错误并且(更重要的是)b) 需要每当调用业务逻辑时,我检查承诺是否已经解决,或者我现在是否需要解决它。在我看来,这只是糟糕的设计。

因此,如果有人能提出更好的解决方案,或者可以用一个好的论据来支持我的一种可能的解决方案,我真的很高兴。

【问题讨论】:

  • 这似乎违反了 GRASP 模式。您可能在将职责分配给类时犯了一些错误。
  • 这当然是可能的。正如我所说,我基本上是在实现一个有限状态机。我有一个保存当前状态的控制器(上下文)。每当控制器收到请求时,它会将其委托给状态,然后状态将返回自身(如果它是循环状态转移)或另一个状态(如果转移)。该“其他状态”必须为该州所知。如果两个状态以循环方式转移到另一个状态(这在 FSM 中绝对常见),它们将需要了解彼此的实例。 Bam - 循环依赖。

标签: php design-patterns oop object


【解决方案1】:

循环依赖是如此恶毒,必须不惜一切代价避免它们,即使这意味着彻底重新思考您的设计并浪费数小时的工作时间。 (从维护程序员的角度讲。无论如何,你还是需要做一些可怜的笨蛋。)

有几种方法可以查看重构代码,但我的简短非特定建议是,任何导致循环依赖的对象都应该将有问题的属性、方法等拆分成一个新对象。那么这两个原始依赖项中的每一个都将依赖于第三个对象,而不是相互依赖。 (并确保这第三个对象不依赖于原件。)

即使这意味着有重复或相似的代码,它仍然比循环依赖更好(但即使这种情况也应该可以通过适当的前期设计来避免。)

【讨论】:

  • 我知道这一点,但我不确定如何打破这种循环依赖——或者是否有可能,因为我试图建模的系统中只是存在循环依赖。不过,我确实理解您的观点 - 我自己是专业的维护程序员,尽管这个问题是关于一个私人项目的。
  • 在阅读了这篇文章和你的其他 cmets 之后......它并没有我想象的那么糟糕。不可取,但如果您正在建模的实际系统固有地具有这种循环依赖关系,那么您可能正在正确建模。
【解决方案2】:

假设您使用的是通常定义的 Facade(作为管理依赖项并使使用复杂 API 变得更容易的简化接口),那么任何“隐藏”(通过外观)类都不应该知道哪些类使用门面。本质上,将其视为一个单一的入口点,这意味着 B 不应该依赖于 A,或者 A 不应该依赖于 Facade。

【讨论】:

  • 我使用术语 Facade 来表示子有限状态机,它处理特别复杂的逻辑并在该逻辑完成后返回(传输)回更简单的有限状态机。这种“转回”实际上是创建循环依赖的地方。我可以重构依赖关系以将其从 B 移动到 F,但它仍然会保留。另请参阅我对康坎评论的评论。
  • 在你描述的情况下,我不认为它是一个门面,那么。看起来它与门面非常接近,但似乎 A->B->F->A (或者无论如何它失败了)设计本质上是有缺陷的。我必须同意其他所有人的观点,感觉就像你需要回顾你的设计并确定如何分离这些依赖关系。
  • 我只是看不出我还能如何模拟一个循环有限状态机,在这种情况下,动态创建出口点是不可能的(出于各种原因)。我的系统看起来基本上是这样的(danielbaulig.de/StateExample.png)。当然,这很简单。 F 实际上要复杂得多,有各种不同的状态,甚至还有额外的子机器。我无法创建没有 F 的 A,也无法创建没有 A 的 F(如果两者在构造函数中都需要彼此)。我想不出任何方法来打破这种循环依赖。甚至可能没有这样的解决方案。
  • 假设您的图表与您的实际设计有共鸣(这非常有用,谢谢),您需要删除 B 对 A 的依赖。我假设两者正试图来回发送消息,或者互相调用方法;您是否考虑过为这两个对象使用某种独立的中间人(而不是 F),从而消除它们之间的依赖关系?本质上,您可以在两者之间建立一个没有依赖关系的订阅者,让每个对象订阅中介,然后注册应该调用的函数作为对某些消息的响应。
  • 使用发布/scubscribe 模式确实可以解耦这些对象。谢谢你在那里启发我。由于在我的情况下每个主题只有一个发布者和订阅者,我相信这有点矫枉过正。虽然我在下面描述的解决方案可能并不完美,但我使用它感觉很舒服。非常感谢您为帮助我所做的一切努力。
【解决方案3】:

虽然我无法解决循环依赖,但我至少确定了一个解决方案,如何创建包含该循环依赖的系统。

我更改了我所有的状态类,不再在构造函数中使用它们的退出点,而是通过一个在第二次调用时基本上会抛出异常的方法。

然后我介绍了一个构建器类,它将在我的状态机中创建每个状态,然后使用上述方法设置它们的状态传输连接。因此,一旦构建器返回有限状态机,它就已完全设置好并可以使用了。每个状态的退出点不能再更改,因为设置它们的方法如果被调用会抛出异常,因为构建器在创建有限状态机时已经调用过一次。

感谢大家在这个问题上帮助我。

【讨论】:

  • 很高兴为您提供帮助,我认为您的设置相当优雅。
猜你喜欢
  • 2021-09-19
  • 1970-01-01
  • 2012-12-22
  • 1970-01-01
  • 2014-07-27
  • 2015-08-09
  • 2014-12-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多