【问题标题】:Observer Pattern: Avoiding bad re-entrant behavior?观察者模式:避免不良的重入行为?
【发布时间】:2013-03-03 19:18:20
【问题描述】:

在实践中,观察者模式的实现如何避免由于重入导致的不良行为?

为了澄清“不良行为”,请考虑模式中的 Subject 在单线程中具有方法 MethodA() 和 MethodB()、事件 OnMethodA() 和 OnMethodB() 以及一个或多个观察者的情况,同步实现:

  • Subject.MethodA() 被调用,
  • Subject 执行它对 MethodA() 所做的操作,然后为所有 Observers 调用 OnMethodA(),
  • Observer1 获取 OnMethodA() 事件,并调用 Subject.MethodB(),
  • Subject 执行它对 MethodB() 所做的操作,然后为所有 Observers 调用 OnMethodB(),

此时,我们正在为所有观察者调用 OnMethodB(),即使我们仍处于 OnMethodA() 通知的中间。这意味着列表中 Observer1 之后的任何观察者都会在“OnMethodA()”之前看到“OnMethodB()”——这是不好的行为。

  • Observer2(由对 Observer1 一无所知的开发人员编写)获取 OnMethodB() 事件,并调用 Subject.MethodA(),
  • ……永远。 Subject.MethodA() -> Observer.OnMethodA() -> Subject.MethodB() -> Observer.OnMethodB() -> Subject.MethodA() -> 等等……

现在你要溢出堆栈了。这是不好的行为。

如果您从一开始就设计了异步、基于队列的通知,或者在通知期间调用 Subject 时抛出异常,您可以避免这种情况,这很容易理解。困扰我的是,我几乎从未将其视为实现该模式的最佳(或真正唯一)实践。您必须已经意识到该问题才能在谷歌上搜索“观察者模式可重入”,并且该搜索的结果似乎只是遇到问题的人,而不是有关模式的书中的警告。

所以我错过了什么吗? 在实践中,观察者模式的实现如何避免由于重入导致的不良行为?

【问题讨论】:

    标签: design-patterns


    【解决方案1】:

    至于避免重入,一个“修复”是设置一个标志isRespondingisUpdating,等等。检查它以避免重入。 p.s.让它volatile

    声称这是一个优雅的甚至是一个好的解决方案。但有时这是一条路要走。

    【讨论】:

      【解决方案2】:

      好吧,我之前从未听说过 Observable 模式的这个问题,所以为此 +1。另一方面,你为什么不看看Java是如何通过ObserverObservablejava.util package中实现这个模式的,另一个注意是这个问题会在现实世界的应用程序中出现,因为这个模式的使用不当,看看这些来自 Martin Fowler 的关于观察者模式的 Gotchas

      【讨论】:

        【解决方案3】:

        这个关于无限递归的问题是观察者模式以及在 GUI 应用程序中使用模型视图控制器 (MVC) 模式时需要注意的一个重要问题。在早期的工作中,我尝试使用使用标志的方法来防止这种情况,但我发现使用标志增加了复杂性,有时无法按预期工作。

        我发现对我有用并且自信地使用了五年多的解决方案是通过确保当您的代码更新视图状态以响应模型更改来打破控制器中的循环事件,您没有观察通过更改视图生成的事件。如果这在您的情况下是不可能的,那么使用旗帜是您最好的选择。

        一个简单的例子是一个模型,它的数据只有一个字符串,视图的文本字段和它们之间的控制器对象。如果用户修改了字符串,这会通知控制器告诉模型更新其字符串。模型更改通知控制器更新文本字段。这是你必须小心的地方。控制器需要能够区分由于用户操作或它自己更新视图内容而生成的视图中的事件。在某些情况下,您可以简单地从视图中选择适当的事件来监听,在其他情况下,您需要让控制器跟踪它是否正在修改视图。

        在这个特定示例中,解决方案是让控制器在文本字段完成编辑时侦听来自文本字段的事件,而不是在文本字段中的文本被修改时。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-20
          • 2023-04-10
          • 1970-01-01
          • 2013-02-12
          相关资源
          最近更新 更多