5

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

为了澄清“不良行为”,请考虑模式中的 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() -> 等等……</li>

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

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

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

4

3 回答 3

1

至于避免重入,一个“修复”是设置一个标志isResponding,,isUpdating等等。检查它以避免重入。ps做到了volatile

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

于 2013-03-04T02:14:05.097 回答
0

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

于 2013-03-04T01:59:34.137 回答
0

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

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

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

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

于 2015-01-23T19:27:18.290 回答