Model-View-Presenter实现思想


34

我试图很好地掌握如何在UI和模型之间实现良好的解耦,但是我在弄清楚究竟在哪里划分线时遇到了麻烦。

我一直在研究Model-View-Presenter,但不确定如何实现它。例如,我的视图有多个对话框。

  • 是否应该有一个View类,其中包含每个对话框的实例?那么在这种情况下,对话框应如何与Presenter交互?即。如果单个对话框需要通过Presenter向模型请求数据,该对话框应如何获得对Presenter的引用?通过引用在施工期间提供给它的视图?
  • 我在想也许视图应该是静态类?然后对话框GetView并从那里获取Presenter ...
  • 我一直在考虑将其设置为具有View和Model所有权的Presenter(而不是具有拥有Presenter和Presenter具有Model的View的所有者),并且Presenter在View中注册事件的回调,但这似乎很多耦合性更高(或至少取决于语言)。

我试图:

  1. 使它尽可能地分离
  2. 理想情况下,可以将Presenter / Model与其他语言的视图结合起来(我还没有做过很多中间语言的工作,但是我知道void(void),至少可以将C#应用程序与C ++库...
  3. 保持代码干净简单

所以..有什么建议应该如何处理交互作用?


您是否看过这篇文章?:en.wikipedia.org/wiki/Model-view-presenter
Bernard

1
我有..我发现它有点快速和高级,我想更好地理解如何在一个大型项目中以尽可能少的耦合来处理多个对话框
。–

Answers:


37

欢迎来到湿滑的斜坡。至此,您已经意识到所有模型-视图交互都将无休止地变化。MVC,MVP(Taligent,Dolphin,Passive View),MVVM仅举几例。

与大多数架构模式一样,Model View Presenter模式也有很多变化和试验机会。所有变体的共同点是,演示者作为视图和模型之间的“中间人”。最常见的两个是“ 被动视图”和“ 监督演示者/控制器” [ Fowler ]。被动视图将UI视为用户和演示者之间非常浅的界面。它包含的逻辑很少(如果有的话),将尽可能多的责任委托给演示者。主管演示者/控制器尝试利用许多UI框架中内置的数据绑定。UI可以处理数据同步,但是演示者/控制器可以介入以实现更复杂的逻辑。无论哪种情况,模型,视图和演示者都构成一个三元组

有很多方法可以做到这一点。通过将每个对话框/表单视为不同的视图来处理此问题是很常见的。在很多情况下,视图和演示者之间是1:1的关系。这不是硬性规定。让一个主持人处理多个相关视图是很常见的,反之亦然。这一切都取决于视图的复杂性和业务逻辑的复杂性。

至于视图和演示者如何获得彼此的引用,有时也称为“ 布线”。您有三种选择:

视图包含对演示者的引用
表单或对话框实现视图。表单具有事件处理程序,这些事件处理程序使用直接函数调用委托给演示者:

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

由于演示者没有对该视图的引用,因此该视图必须向其发送数据作为参数。演示者可以使用视图必须侦听的事件/回调函数与视图进行通信。

Presenter拥有要查看的引用。
在此方案中,视图向用户公开了它显示的数据的属性。演示者侦听事件并操纵视图上的属性:

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

两者相互引用,形成循环依赖关系
这种情况实际上比其他情况更容易使用。该视图通过调用演示者中的方法来响应事件。演示者通过公开的属性从视图中读取/修改数据。

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

MVP模式还需要考虑其他问题。创建顺序,对象寿命,进行布线的地方,MVP三合会之间的通信,但是这个答案已经足够长了。


1
这绝对是有帮助的。现在,我已经掌握了其中的一些要素,因此三合一与终生之间的交流是我目前遇到的麻烦。
trycatch 2011年

8

就像每个人都说过的那样,这里有数十种观点,而且没有一个是对还是错。在不涉及多种模式的情况下,仅关注MVP,这里有一些实施建议。

将它们分开。视图应该实现一个接口,该接口形成视图和演示者之间的纽带。视图创建了一个演示者,并将其自身插入到演示者中,并公开了它为演示者提供的与视图交互的方法。视图负责以其希望的任何方式实现这些方法或属性。通常,您有一个视图:一个演示者,但在某些情况下,您可以有多个视图:一个演示者(Web,WPF等)。这里的关键是演示者不知道UI实现,只能通过界面与视图交互。

这是一个例子。首先,我们有一个带有简单方法的视图类,用于向用户显示消息:

interface IView
{
  public void InformUser(string message);
}

现在是主持人。请注意,演示者将IView纳入其构造函数。

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

现在是实际的用户界面。可能是窗口,对话框,网页等。没关系。请注意,视图的构造函数将通过将自身注入演示者来创建演示者。

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

演示者不在乎视图如何实现它所执行的方法。对于演示者所知,它可能正在写入日志文件,甚至没有向用户显示。

无论如何,演示者都会在后端使用模型进行一些工作,并且有时希望将发生的事情告知用户。因此,现在演示者中的某处有一个方法可以调出视图InformUser消息。

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

这是您去耦的地方。演示者仅持有IView的实现参考,并不真正在意它的实现方式。

这也是一个糟糕的实现,因为您确实在视图中引用了Presenter,并且对象是通过构造函数设置的。在更强大的解决方案中,您可能希望查看控制反转(IoC)容器,例如Windsor,Ninject等,这些容器将在运行时按需为您解决IView的实现,从而使其更加分离。


4

我认为重要的是要记住,控制器/演示器是真正执行操作的地方。由于必要,控制器中的耦合是不可避免的。

Controller的核心在于,如果您对View进行更改,则不必更改Model,反之亦然(如果Model更改,View也不必更改),因为Controller是翻译在视图中建模,然后再次返回。但是,当Model或View发生更改时,Controller也会更改,因为您必须有效地在Controller内转换如何查看View的模型,以及如何将View中的更改返回到Mode。

我能给出的最好的例子是,当我编写一个MVC应用程序时,不仅能够在GUI视图中存储数据,而且还可以编写一个例程,将从模型中提取的数据推入string调试器中显示的程序中。 (并扩展为纯文本文件)。如果我可以获取模型数据并将其自由转换为文本,而无需更改视图或模型,而只需更改控制器,那么我就走了正确的道路。

话虽如此,您必须在不同组件之间具有引用才能使其全部正常工作。Controller需要了解View来推送数据,View需要了解Controller以便在进行更改时告诉它(例如,当用户单击“ Save”或“ New ...”时)。控制器需要了解模型以提取数据,但我认为模型不应该知道其他任何信息。

警告:我来自完全Mac,Objective-C,可可的背景,无论您是否愿意,它确实将您带入MVC范例。


这绝对是我的目标。我的主要问题是如何设置视图-它是否应该是包含每个对话框的实例的类,然后使用调用Dialog.Getters的View.Getters,或者演示者是否应该能够直接调用Dialog.Getters(这似乎耦合得太紧密了,所以大概不是吗?)
trycatch 2011年

我认为Presenter / Controller应该对Views负全部责任,因此后者应该负责。同样,一定会发生一些耦合,但是至少如果责任的方向是明确的,那么从长远来看,维护应该更容易。
菲利普·里根

2
我当然同意P / C应该负责View,但是我认为使MVP强大的部分原因是能够拉出整个UI库并插入新的UI库并进行某种按摩(dllimporting和诸如此类的东西)能够在该位置运行另一个。通过Controller / Presenter直接访问对话框,这不会更困难吗?我当然不是想吵架,只是要进一步理解:)
trycatch 2011年

我认为真正的力量来自两个方向:第一个是View和Model与其他没有任何关系,第二个是大多数开发工作(应用程序的引擎)是在一个整齐的内部完成的单元,即控制器。但是必然会发生一些责任流失。至少大多数接口交换将在Controller中完成,并且从View进行的任何链接都将是最少的。正如其他人所说,逻辑的某些渗漏是可以预料的,也是允许的。MVC不是魔术子弹。
菲利普·里根

解耦的重点在于,演示者仅通过定义明确的界面(独立于UI库)访问视图,因此这就是将UI库替换为另一个库的方法(另一个将为表单/窗口/对话框/页面实现相同的接口) / control / whatever)
Marcel Toth 2012年

2

通常,您希望模型封装与该模型的所有交互。例如,您的CRUD操作(创建,读取,更新,删除)都是模型的一部分。特殊计算也是如此。这样做有两个很好的理由:

  • 自动化此代码的测试更加容易
  • 它把所有重要的东西都放在一个地方

在控制器(MVC应用程序)中,您要做的就是收集需要在视图中使用的模型,并在模型上调用适当的函数。对模型状态的任何更改都发生在此层中。

您的视图仅显示您准备的模型。本质上,视图仅读取模型并相应地调整其输出。

将一般原理映射到实际类

请记住,您的对话框是视图。如果您已经有一个对话框类,则没有理由创建另一个“视图”类。Presenter层实质上将模型绑定到View中的控件。业务逻辑和所有重要数据都存储在模型中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.