3

在支持 GC 的语言中,当观察者订阅主题的事件时,实际上主题得到了观察者的引用。

因此,在删除观察者之前,它必须先取消订阅。否则,因为它仍然被主题引用,它永远不会被垃圾收集。

通常有3种解决方案:

  1. 手动取消订阅
  2. 弱参考。

它们都会导致其他问题。

所以通常我不喜欢使用观察者模式,但我仍然找不到任何替代品。

我的意思是,这种模式以如此自然的方式描述事物,以至于您几乎找不到更好的方法。

你怎么看待这件事?

4

3 回答 3

1

在这种情况下,您可以finalize()在 Java 中使用。当您必须释放资源(如数据库连接)finalize()时,这是一个坏主意,因为某些外部系统受到影响。在您的情况下,安装观察者的对象将在您的应用程序运行时进行 GC,然后将被调用并且它可以取消订阅观察者。finalize()

不完全是您想要的,但必须有人决定“现在可以退订”。当您的主题消失(但它应该已经杀死所有观察者)或安装观察者的对象时,会发生这种情况。

如果您的应用程序意外终止,那么finalize()在这种情况下可能不会调用它并没有什么坏处。

于 2009-11-11T16:50:11.307 回答
0

如果你想删除一个观察者,你应该首先通过取消订阅来通知发布者,否则它会尝试发送事件,并且根据它的编写方式,它可能会使应用程序崩溃,悄悄地忽略错误或删除观察者。但是,如果您打开某些东西,请关闭它;如果您订阅,请取消订阅。

IMO,您没有取消订阅的事实是一个糟糕的设计。不要将模式实施不当归咎于模式。

观察者模式效果很好,但如果你想缓解一些问题,你可以使用 AOP 来实现: http ://www.cin.ufpe.br/~sugarloafplop/final_articles/20_ObserverAspects.pdf

于 2009-11-11T16:51:03.053 回答
0

考虑一个对象的场景,它计算某些可观察事物变化的频率。对象的引用有两种类型:(1)那些对计数感兴趣的实体的引用;(2) 那些被可观察事物使用的那些对计数并不真正感兴趣但需要更新它的东西。对计数感兴趣的实体应该持有对对象的引用,该对象又持有对管理计数的对象的引用。必须更新计数但对它并不真正感兴趣的实体应该只保存对第二个对象的引用。

如果第一个对象包含一个终结器,它将在对象超出范围时被触发。这可能会触发第二个对象取消订阅,但它可能不应该直接取消订阅。取消订阅可能需要获取锁,并且终结器不应该等待锁。相反,第一个对象的终结器可能应该将该对象添加到使用 Interlocked.CompareExchange 维护的链接列表中,并且一些其他线程应该定期轮询该列表以查找需要取消订阅的对象。

注意,顺便说一句:如果第一个对象持有对第二个对象的引用,则当第一个对象的终结器运行时,后者将保证存在,但不能保证处于任何特定状态。除了取消订阅之外,清理线程不应尝试对其进行任何操作。

于 2010-09-30T15:55:34.347 回答