观察者模式:避免不良的重入行为?

时间:2013-03-03 19:18:20

标签: design-patterns

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

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

  • 调用Subject.MethodA(),
  • 主题执行它对MethodA()的操作,然后为所有观察者调用OnMethodA(),
  • Observer1获取OnMethodA()事件,并调用Subject.MethodB(),
  • 主题执行它对MethodB()的操作,然后为所有观察者调用OnMethodB(),

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

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

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

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

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

3 个答案:

答案 0 :(得分:1)

至于避免重入,一个“修复”是设置标志isRespondingisUpdating,无论如何。检查它以避免重入。附:把它volatile

声称这是一个优雅甚至是一个好的解决方案。但有时这是一种方法。

答案 1 :(得分:0)

我之前从未听过Observable模式的这个问题。另一方面,为什么不看看Java如何通过java.util packageObserverObservable中实现此模式,另一个注意事项是这个问题会在现实世界的应用程序中发生,因为如果不正确地使用这种模式,请查看Martin Fowler对观察者模式的这些Gotchas

答案 2 :(得分:0)

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

我发现为我工作并且已经使用超过五年的解决方案是通过确保在代码更新视图状态时响应模型更改来打破控制器中的循环事件,您没有观察通过更改视图生成的事件。如果在您的情况下无法做到这一点,那么使用旗帜是最好的选择。

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

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