相对较新的模式,让我直接在WinForms的上下文中展示一个例子。
我有一个基本的MVP被动视图结构,我应该继续使用它:
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();
}
}
我的问题是:
1)谁应该知道模型User
,View或Presenter的具体实例?
2)在这种情况下会有什么好处?
3)假设我的模型从不依赖于视图。在那种情况下,如果View知道Model有什么问题?在所有UserView
出现后UserModel
不是吗?
4)如果Presenter只与Model和View的接口进行交互,那么在model.Save
eventhandler中调用Save
,我从哪里获取Model
的具体实例?
答案 0 :(得分:17)
严格来说,您应该遵守以下规则:
Presenter通常通过处理View引发的事件来协调Model和View之间的所有通信。所以回答你的问题:
1)谁应该知道模型User,View或Presenter的具体实例?
理想情况下,都不是。 Presenter应该通过IUserModel接口与UserModel进行通信。具体实例被注入Presenter(例如通过其构造函数)。
2)在这种情况下会有什么好处?
主要好处是自动化单元测试。您可以将模拟模型或视图注入到单独的测试单元中。
3)假设我的模型从不依赖于视图。在那种情况下,如果View知道Model有什么问题?在所有UserView出现后,UserModel不是吗?
它没有任何内在错误。 MVP的变体支持从View到Model的直接通信,通常是利用数据绑定。您将失去一些可测试性,以换取不必从头开始编写绑定代码。
4)如果Presenter只与Model和View的接口交互,那么在Save eventhandler中调用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();
}
}
答案 1 :(得分:7)
如果查看模型有什么问题?完成所有UserView之后 专门针对UserModel不是吗?
无。这是MVP模式的Supervising Controller
变体中的公认惯例。视图直接与模型交互以进行简单操作,而更复杂的操作则通过演示者进行编组。在Passive View
中,所有内容都通过演示者。
此外,请参阅Jeremy Miller的 构建您自己的CAB系列 ,以便更好地了解这两种方法之间的差异:Supervising Controller和{{3} }。
答案 2 :(得分:3)
Presenter应该知道模型,View不应该知道。在许多用户界面应用程序中,presententation层是一个好主意。表示层只是一个适配器。它提供了一个易于用户界面层使用的界面(即,它呈现了大量事件,可绑定属性等),同时模糊了底层数据层。这使数据层更容易重复使用。
修改强>
那为什么视图不能直接与模型对话?当然可以。问题是模型和视图之间通常存在阻抗不匹配。换句话说,对于视图使用而言自然的编程接口与模型公开的接口不匹配。如果您调整模型以满足视图的需要,那么您最终会在模型和您正在使用的特定类型的界面之间创建强大的耦合。
例如,您的应用程序今天可能是一个GUI应用程序,但是如果明天您被要求为云生成一个版本呢?当您尝试切换到WCF Rest时,对Winforms有用的事件和可绑定属性将受到阻碍。如果您使用表示层,那么将代码调整到新环境将更容易
。答案 3 :(得分:1)
如果您对介绍模式的介绍不是太多,我建议您先看看Presenter-first MVP的变种。
在此变体中,并提供问题的答案,演示者只知道模型和视图,但只能通过接口。视图和模型都不知道彼此。主持人通过事件和方法协调每个人。
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());
}
}
非常基本的例子,但说明了这一点。演示者仅仅是视图和模型之间通信的渠道。
创建演示者可以使用穷人的DI或适当的容器来完成:
Presenter p = new Presenter(new CustomerModel(), new CustomerForm());
请注意,AtomicObject建议不要引用presenter,因此它实际上如下所示:
new Presenter(existingCustomerModel, existingCustomerForm);
existingCustomerModel.Initialise();
模型和视图具有范围的事实意味着演示者也通过其引用保持在范围内......聪明。