2013-03-03 44 views
4

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

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

  • Subject.MethodA()被调用,
  • 主题做什么它为methodA(),然后调用OnMethodA()为所有的观察员,
  • Observer1得到OnMethodA( )事件,并调用Subject.MethodB(),
  • 该主题做什么它的方法b(),那么对于所有的观察员呼吁OnMethodB(),

在这一点上,我们称之为OnMethodB()所有观察员,尽管我们仍然在通知中的中间为OnMethodA()。这意味着列表中的Observer1之后的任何观察者都将在“OnMethodA()”之前看到“OnMethodB()” - 这是不好的行为。

  • Observer2(由谁不知道什么Observer1一个开发者编写)得到OnMethodB()事件,并调用Subject.MethodA(),
  • ...永远。 Subject.MethodA() - > Observer.OnMethodA() - > Subject.MethodB() - > Observer.OnMethodB() - > Subject.MethodA() - >等...

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

如果你已经设计了异步,基于队列的通知,从一开始,或通知期间抛出的通话主题的例外,就可以避免这一点,这是很容易理解。有什么困扰我的是,我几乎从来没有把这种做法看作是实施该模式的最佳(或者真的,唯一的)做法。你必须已经意识到谷歌“观察者模式可重入”的问题,并且搜索结果似乎只是遇到问题的人,而不是关于模式的书中的警告。

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

回答

0

好吧,我从来没有听说过这个问题有可观察的模式之前,所以+1了点。在另一方面你为什么不看的Java是如何通过ObserverObservable实现在java.util package这种模式,另一个值得注意的是,将会发生,因为这种模式的使用不当的实际应用这个问题,看看这些Gotchas从观察者模式的马丁福勒。

1

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

我是不是声称这是一个优雅或甚至是一个很好的解决方案。但有时候这是一条路。

0

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

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

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

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