如何在不保持视图活动的情况下实现 MVC 模式

发布于 2024-09-11 14:46:55 字数 902 浏览 4 评论 0原文

我想在困难的情况下实现 MVC 模式。困难在于我的模型(生成事件的实体)是长期存在的,而视图(事件的接收者)是短暂的。我的问题是,通过两者之间的连接,长期存在的模型使我短暂的视图保持活动状态,即它们不能被垃圾收集。

  [MODEL] ------- <weak> -------> [VIEW]
     |
 <strong>
     |
     v
[CONTROLLER]

解决此问题的一种方法是将模型中的连接存储在 WeakHashMap中。这本质上是让 View 被垃圾回收,当发生这种情况时,WeakHashMap 也会将相应的 Controller 扔掉。也就是说,如果控制器不持有对视图的(强)引用——它通常会这样做。在这种情况下,视图通过强引用保持活动状态,直到模型超出范围。

  [MODEL] ------- <weak> -------> [VIEW]
     |                               ^
 <strong>                            |
     |                               |
     v                               |
[CONTROLLER] ----------- <strong> ---/

是否有另一种方法可以将侦听器附加到我的模型,但不会使我的视图(和控制器)保持活动状态?

更新:回答mdma的问题:控制器保留对视图的引用,因为它需要更新视图。此引用可能很弱,但我希望控制器成为 View 类的匿名内部类,在这种情况下,Controller 实例具有对 View 实例的隐式强引用。

I'd like to implement the MVC pattern in a difficult situation. The difficulty is that my Models (the entities generating the events) are long-lived, while the Views (the receivers of the events) are short-lived. My problem is that through the connection between the two, the long-lived Models keep my short lived Views alive, i.e. they cannot be garbage-collected.

  [MODEL] ------- <weak> -------> [VIEW]
     |
 <strong>
     |
     v
[CONTROLLER]

A way to work around this is to store the connections in the Model in a WeakHashMap<View, Controller>. This essentially lets the View to be garbage collected, and when that happens, the WeakHashMap will throw the corresponding Controller out, too. That is, if the Controller doesn't hold a (strong) reference to the View -- which it usually does. In this case the Views are kept alive through the strong references until the Model goes out of scope.

  [MODEL] ------- <weak> -------> [VIEW]
     |                               ^
 <strong>                            |
     |                               |
     v                               |
[CONTROLLER] ----------- <strong> ---/

Is there another way to attach listeners to my models that won't keep my views (and controllers) alive?

UPDATE: To answer mdma's question: the Controller keeps a reference to the View, because it needs to update the View. This reference can be weak, but I would like to have the Controllers to be anonymous inner-classes of the View class, in which case the Controller instance has an implicit strong reference to the View instance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

謸气贵蔟 2024-09-18 14:46:55

有几种方法可以实现 MVC。

  • 编写一个模型,然后让您的视图监听模型的更改。当发生任何事情时,视图会通知控制器。
  • 编写一个视图,然后让您的模型监听视图的更改。当发生任何事情时,视图会通知控制器。
  • 写一个视图。让您的模型监听视图的变化。让你的控制器监听视图,如果发生任何事情,它将引发不同的事件。

最后一个给出了视图、控制器和模型之间最弱的耦合。对控制器进行单元测试是一个混蛋,因为您最终必须存根事件处理程序。您甚至无法使用模拟框架来模拟它们,因为您需要始终引发事件。不过,它确实有效。

我喜欢 MVCP:

  • 允许您的控制器将模型包装在演示者中。控制器侦听附加的视图,并每次为它们提供一个新的演示者。 Presenter 将字段更改委托给 Model,还将命令委托给 Controller。控制器和模型都不依赖于演示者的引用。当视图消失时,演示者也会随之消失。

演示者的伟大之处在于您可以封装视图所需的内容。演示者的界面几乎完全由视图驱动。您甚至可以为不同的视图创建不同的演示者,并通过一个接口方法填充它们,如下所示:Presenter.PopulateWith(model,controller)。这为您提供了一个执行所有表示逻辑(将日期转换为字符串、没有 . 的登录名等)的好地方,而不会污染您可爱的模型。并且您可以免费获得弱参考!

这与现在惯用的 WPF 中使用的 MVVM 模式非常相似。在 Java 中运行良好,在 Web 中也运行良好。无论如何,希望这些能给你一些想法。

There are a few ways to do MVC.

  • Write a model then let your view listen to changes to the model. The view tells the controller when anything happens.
  • Write a view then let your model listen to changes to the view. The view tells the controller when anything happens.
  • Write a view. Let your model listen to changes to the view. Let your controller listen to the view, which will raise different events if anything happens.

The last one gives the weakest coupling between views, controllers and models. It's a bastard to unit-test the controller because you end up having to stub event handlers. You can't even mock them using mocking frameworks, because you need to raise events the whole time. It does work, though.

I like MVCP:

  • Allow your controller to wrap the Model in a Presenter. The Controller listens out for Views being attached, and hands them a new Presenter each time. The Presenter delegates field changes to the Model, and also delegates commands to the Controller. Neither the Controller nor the Model hangs on to a reference to the Presenter. When the view dies, the presenter goes with it.

The great thing about presenters is that you can encapsulate just the stuff that the view needs. The interface for a presenter is almost entirely driven by the view. You can even do stuff like create different presenters for different views, and populate them all through one interface method like this: Presenter.PopulateWith(model, controller). This gives you a great place to do all the presentation logic (dates into strings, login names without the ., etc.) without polluting your lovely Model. And you get your weak reference for free!

This is very similar to the MVVM pattern now used in idiomatic WPF. Works well in Java, also with Web. Hope these give you some ideas, anyway.

日记撕了你也走了 2024-09-18 14:46:55

这里您已经获得了 MVC 模式的出色实现。您的问题可能有解决方案。

Here you've got a great implementation of the MVC pattern. There is probably a solution for your problem.

山有枢 2024-09-18 14:46:55

我认为你的方向非常正确。我看到两种可能的解决方案来正确回收视图:

  1. 将视图生命周期设计到系统中,以便显式地完成视图解构,然后所有感兴趣的各方都可以释放对视图的引用。

  2. 删除控制器中的强引用。控制器可以使用 Wea​​kReference 来保留视图,每次访问都必须检查该视图,或者向控制器传递一个委托给真实视图的 View 实现,通过弱引用保留它。如果引用已被回收(为空),则方法调用是无操作的。

I think you're very much on the right track. I see two possible solutions to getting views properly reclaimed:

  1. Design the view lifetime into the system, so that view descruction is done explicitly, and all interested parties can then release their references to the view.

  2. Remove the strong reference in the controller. The controller can either use a WeakReference to hold on to the view, which must be checked with each access, or instead, pass the controller a View implementation that delegates to your real view, holding on to it via a weak reference. If the reference has been reclaimed (is null) the method call is a no-op.

倾城泪 2024-09-18 14:46:55

...但我希望控制器成为 View 类的匿名内部类,在这种情况下,Controller 实例具有对 View 实例的隐式强引用。

根据问题中的图表,这根本行不通。

Model 中对 Controller 的常规引用以及 Controller 中对 View 的常规引用足以意味着 View< /code> 是强可达的。因此,Controller 中对 View 的弱引用不会被破坏......直到 Model 本身符合垃圾回收条件。

由于匿名内部类永远不可能是静态的,因此您没有明智的选择 (*),只能使 Controller 成为静态嵌套类或非嵌套类。

另一种选择是使从模型到控制器的链接成为弱引用。

(* 实际上,有一个可能有效的技巧...尽管它太可怕了,无法提及。您可以找出保存 Controller 对象链接的隐藏属性的名称到其父对象,也许可以使用反射来查找 Field,然后使用它将属性设置为 null。)


编辑

这就是 JLS 关于匿名类的说法 - JLS 15.9.1

匿名类从来都不是抽象的(第 8.1.1.1 节)。匿名类始终是内部类(第 8.1.3 节); 它永远不是静态的(§8.1.1,§8.5.2)。匿名类始终是隐式最终的(第 8.1.1.2 节)。

我很难将这一点与OP的评论相协调......

... but I would like to have the Controllers to be anonymous inner-classes of the View class, in which case the Controller instance has an implicit strong reference to the View instance.

That simply won't work ... based on the diagrams in the question.

A regular reference in the Model to the Controller and another in the Controller to the View will be sufficient to mean that the View is strongly reachable. As a result the weak reference in the Controller to the View won't be broken ... until the Model itself becomes eligible for garbage collection.

Since an anonymous inner class can never be static, you have no sensible choice (*) but to make the Controller a static nested class or a non-nested class.

The other alternative would be to make the link from the Model to the Controller a weak reference.

(* Actually there is an trick that might possibly work ... though it is almost too horrible to mention. You could figure out what the name of the hidden attribute that holds the Controller object's link to its parent object, and maybe use reflection to find the Field and then use that to set the attribute to null.)


EDIT

This is what the JLS says about anonymous classes - JLS 15.9.1

An anonymous class is never abstract (§8.1.1.1). An anonymous class is always an inner class (§8.1.3); it is never static (§8.1.1, §8.5.2). An anonymous class is always implicitly final (§8.1.1.2).

I'm having difficulty reconciling this with the OP's comment ...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文