it-swarm.cn

Model-View-Presenter实现思想

我试图很好地掌握如何在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. 保持代码干净简单

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

34
trycatch

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

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

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

关于视图和演示者如何获得彼此的引用,有时称为wiring。您有三种选择:

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

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

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

演示者持有对视图的引用
在场景中,视图向用户显示其显示的数据的属性。演示者侦听事件并操纵视图上的属性:

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三合会之间的通信,但是这个答案已经足够长了。

37
Kenneth Cochran

就像每个人都说过的那样,这里有数十种观点,而且没有一个是对还是错。在不涉及多种模式的情况下,仅关注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的实现,从而使其更加分离。

8
Bil Simser

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

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

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

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

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

4
Philip Regan

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

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

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

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

将通用原理映射到实际类

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

2
Berin Loritsch