7

相对较新的模式,让我直接在 WinForms 的上下文中展示一个示例。

我有一个基本的 MVP Passive View 结构,我应该继续使用它:

public partial class UserView : Form, IUserView
{
    public event EventHandler Save;

    public UserView()
    {
        InitializeComponent();

        new UserPresenter(new UserModel(), this);
    }
}

public class UserPresenter
{
    public UserPresenter(IUser model, IUserView view)
    {
        view.Save += (sender, e) => model.Save();
    }
}

或者

public partial class UserView : Form, IUserView
{
    public event EventHandler Save;

    public UserView()
    {
        InitializeComponent();

        new UserPresenter(this);
    }
}

public class UserPresenter
{
    public UserPresenter(IUserView view)
    {
        var model = new UserModel();
        //assuming I have the logic to bind property values from View to Model
        view.Save += (sender, e) => model.Save();
    }
}

我的问题是:

User1) 谁应该知道 model 、 View 或 Presenter的具体实例?

2)在这种情况下会有什么好处?

3)假设我的模型从不依赖于视图。在那种情况下,如果 View 知道 Model 有什么问题?毕竟UserView是为了呈现UserModel不是吗?

4)如果Presenter应该只与Model和View的接口交互,那么要调用eventhandler,我model.SaveSave哪里得到具体的实例Model

这里这里有两个重复的问题,但我猜它们并没有完全处理我的场景。

4

4 回答 4

17

严格来说,你应该有以下规则:

  1. 模型不知道视图或演示者。
  2. View 不知道 Model 或 Presenter。
  3. Presenter 知道模型和视图,但只能通过它们的接口。

Presenter 协调模型和视图之间的所有通信,通常通过处理视图引发的事件。所以回答你的问题:

1) 谁应该知道模型 User、View 或 Presenter 的具体实例?

理想情况下,两者都不是。Presenter 应该通过 IUserModel 接口与 UserModel 通信。具体实例被注入到 Presenter 中(例如通过它的构造函数)。

2)在这种情况下会有什么好处?

主要好处是自动化单元测试。您可以注入模拟模型或视图来单独测试单元。

3)假设我的模型从不依赖于视图。在那种情况下,如果 View 知道 Model 有什么问题?毕竟 UserView 是为了呈现 UserModel 不是吗?

它本质上没有任何问题。MVP 的变体支持从视图到模型的直接通信,通常是为了利用数据绑定。您失去了一些可测试性以换取不必从头开始编写绑定代码。

4)如果Presenter应该只与Model和View的接口交互,那么在Save事件处理程序中调用model.Save,我从哪里得到Model的具体实例?

依赖注入,如下图所示的简化示例。

public class SamplePresenter
{
     public SamplePresenter(ISampleModel model, ISampleView view)
     {
          view.Saved += (sender, e) => model.Save();
     }
}

public interface ISampleModel
{
     void Save();
}

public interface ISampleView
{
     void Show();
     event EventHandler Saved;
}

public class Program
{
     [STAThread]
     static void Main()
     {
          ISampleModel model = new SampleModel();
          ISampleView view = new SampleView();
          SamplePresenter presenter = new SamplePresenter(model, view);
          view.Show();
     }
}
于 2013-01-18T19:12:22.113 回答
7

如果视图知道模型有什么问题?毕竟 UserView 是专门为 UserModel 制作的,不是吗?

没有。Supervising Controller在 MVP 模式的变体中,这是公认的做法。视图直接与模型交互以进行简单操作,而更复杂的操作则通过演示者进行编组。在 中Passive View,一切都通过演示者。

此外,请参阅 Jeremy Miller 的构建您自己的 CAB 系列,以更好地了解这两种方法之间的差异:监督控制器被动视图

于 2013-01-17T19:58:04.747 回答
3

演示者应该知道模型,视图不应该。在许多用户界面应用程序中,表示层是一个好主意。表示层只是一个适配器。它提供了一个易于用户界面层使用的界面(即,它提供了许多事件、可绑定属性等),同时隐藏了底层数据层。这使得数据层更容易重用。

编辑

那么为什么视图不能直接与模型对话呢?它当然可以。问题是模型和视图之间通常存在阻抗不匹配。换句话说,视图自然使用的编程接口与模型自然公开的接口不匹配。如果您调整模型以适应视图的需求,那么您最终会在模型和您正在使用的特定类型的界面之间创建强耦合。

例如,您的应用程序今天可能是一个 GUI 应用程序,但如果明天您被要求为云生成一个版本怎么办?当您尝试切换到 WCF Rest 时,对 Winforms 有帮助的事件和可绑定属性只会妨碍您。如果您使用表示层,那么使您的代码适应新环境会容易得多

于 2013-01-17T19:38:20.063 回答
1

如果您对演示模式的介绍还不算多,我建议您看一下 MVP 的 Presenter-first 变体。

在这个变体中,并为您的问题提供答案,演示者知道模型和视图,但只能通过接口。视图和模型都不知道彼此。演示者通过事件和方法来协调每一个。

http://atomicobject.com/pages/presenter+first

http://spin.atomicobject.com/2008/01/30/presenter-first-get-your-triads-talking/

例子:

Class Presenter {
    private IModel model;
    private IView view;

    void Presenter(IModel model, IView view) {
        _model = model;
        _view = view;
    }

    void Initialise() {
        // Attach handler to event view will raise on save
        _view.OnSave += HandleViewSave();
    }

    void HandleViewSave(){
        _model.Save(_view.GetStuffToSave());
    }
}

非常基本的例子,但说明了这一点。演示者只是视图和模型之间通信的管道。

可以使用 Poor Man 的 DI 或适当的容器来创建演示者:

Presenter p = new Presenter(new CustomerModel(), new CustomerForm());

请注意,AtomicObject 建议不要引用 Presenter,因此它实际上看起来像这样:

new Presenter(existingCustomerModel, existingCustomerForm);

existingCustomerModel.Initialise();

模型和视图具有范围这一事实意味着演示者也通过其引用保持在范围内......聪明。

于 2013-01-17T19:44:30.320 回答