如何为Winforms解决方案设置MVP?


14

我过去曾经使用过MVP和MVC,我更喜欢MVP,因为我认为它可以更好地控制执行流程。

我已经创建了我的基础结构(数据存储/存储库类),并且在对示例数据进行硬编码时可以毫无问题地使用它们,因此现在我转向GUI并准备我的MVP。

A节

  1. 我已经看到MVP使用视图作为入口点,也就是说,在视图构造方法中,它创建了演示者,该演示者又创建了模型,并根据需要连接事件。

  2. 我还以演示者为切入点,在其中创建了视图,模型和演示者,然后在其构造函数中为该演示者提供了视图和模型对象以关联事件。

  3. 与2中一样,但是模型没有传递给演示者。相反,该模型是一个静态类,在其中调用方法并直接返回响应。

B区

在使视图和模型保持同步方面,我已经看到了。

  1. 每当视图中的值发生更改时,即TextChanged.Net / C#中的事件。这会触发一个DataChangedEvent传递到模型中的,以使其始终保持同步。在模型发生变化的地方(即它侦听的后台事件),然后通过引发的相同想法来更新视图DataChangedEvent。当用户想要提交更改SaveEvent时,它将触发,并进入模型进行保存。在这种情况下,模型将模仿视图的数据并处理动作。

  2. 与#b1相似,但是视图并非始终与模型同步。相反,当用户想要提交更改时,将SaveEvent被解雇,演示者获取最新的详细信息并将其传递到模型中。在这种情况下,除非需要对视图数据进行操作,否则模型将不知道视图数据,在这种情况下,将传递所有必需的详细信息。

C区

在视图中显示业务对象,即不是原始数据的对象(MyClass)(int,double)

  1. 该视图具有将显示为域/业务对象的所有数据的属性字段。例如,view.Animals公开一个IEnumerable<IAnimal>属性,即使视图将它们处理到TreeView中的Nodes中。然后对于选定的动物,它将SelectedAnimal作为IAnimal属性公开。

  2. 该视图不了解域对象,它仅公开图元/框架(.Net / Java)包含的对象类型的属性。在这种情况下,演示者将把适配器对象传递给域对象,然后适配器将给定的业务对象转换为视图上可见的控件。在这种情况下,适配器必须有权访问视图上的实际控件,而不仅仅是任何视图,因此会变得更加紧密。

D区

用于创建单个控件的多个视图。即,您有一个具有简单模型的复杂视图,例如保存不同类型的对象。您可以在侧面有一个菜单系统,每单击一个项目就会显示相应的控件。

  1. 您创建一个巨大的视图,其中包含通过视图界面公开的所有单个控件。

  2. 您有几种看法。您有一个菜单视图和一个空白面板。该视图创建所需的其他视图,但不显示它们(visible = false),该视图还为其包含的每个视图(即子视图)实现接口,因此可以向一个演示者公开。空白面板中将填充其他视图(Controls.Add(myview))和((myview.visible = true)。这些“子”视图中引发的事件由父视图处理,父视图又将事件传递给演示者,反之亦然,以将事件提供给子元素。

  3. 每个视图(无论是主要父视图还是较小的子视图)均连接到自己的演示者和模型中。您可以从文化上将视图控件放到现有的表单中,它将具备功能,只需将其连接到幕后的演示者中即可。

E节

如果所有内容都具有接口,那么现在基于以上示例中MVP的完成方式,由于它们可能不是交叉兼容的,因此将影响此答案。

  1. 一切都有界面,视图,演示者和模型。然后,显然每个都有具体的实现。即使您只有一个具体的视图,模型和演示者。

  2. 视图和模型具有接口。这允许视图和模型不同。演示者创建/获得视图和模型对象,并且仅用于在它们之间传递消息。

  3. 仅视图具有界面。该模型具有静态方法,不会创建,因此不需要接口。如果需要其他模型,则演示者将调用一组不同的静态类方法。由于模型是静态的,因此与演示者没有链接。

个人想法

从我介绍的所有不同变体中(大多数我可能已经以某种形式使用过),我确信还有更多。我更喜欢A3,因为它可以使业务逻辑在MVP之外可重用,而B2则可以减少数据重复和触发事件。C1用于不添加其他类,请确保将少量非单元可测试的逻辑放入视图中(如何可视化域对象),但是可以对其进行代码审查,也可以在应用程序中简单地对其进行查看。如果逻辑很复杂,我会同意一个适配器类,但并非在所有情况下都同意。对于D部分,我觉得D1创建的视图对于菜单示例来说至少太大了。我以前用过D2和D3。D2的问题是,您最终不得不编写大量代码才能将事件往返于演示者与演示者之间路由到正确的子视图,并且其拖放操作不兼容,每个新控件都需要更多接线才能支持单个演示者。D3是我的首选,但是即使视图恰好非常简单或不需要重用,D3仍会作为演示者和模型添加更多类来处理该视图。我认为根据情况,D2和D3的混合效果最好。对于E部分,我认为具有接口的所有内容都可能过头了,因为我已经对域/业务对象进行了此操作,因此这样做通常不会在“设计”中看到任何优势,但它确实有助于在测试中模拟对象。我个人认为E2是经典的解决方案,尽管我以前在2个项目中使用过E3。我认为根据情况,D2和D3的混合效果最好。对于E部分,我认为具有接口的所有内容都可能会过时了,我已经对域/业务对象进行了此操作,并且这样做通常不会在“设计”中看到任何优势,但是它确实有助于在测试中模拟对象。我个人认为E2是经典的解决方案,尽管我以前在2个项目中使用过E3。我认为根据情况,D2和D3的混合效果最好。对于E部分,我认为具有接口的所有内容都可能会过时了,我已经对域/业务对象进行了此操作,并且这样做通常不会在“设计”中看到任何优势,但是它确实有助于在测试中模拟对象。我个人认为E2是经典的解决方案,尽管我以前在2个项目中使用过E3。

我是否正确实施MVP?有正确的方法吗?

我已经读过Martin Fowler的著作,其中有很多变化,而且我还记得当我刚开始做MVC时,我理解了这个概念,但是最初无法确定切入点在哪里,一切都有自己的功能,但是控制和创建原始作品的是什么MVC对象集。


2
问这个问题的原因是,我希望几乎在第一次尝试时都能正确解决。我宁愿遵循一个标准的MVP,也不愿对同一模式使用不同的变体创建6个应用程序。
JonWillis

太好了...我想问了很长时间
2011年

Answers:


4

您在此处呈现的很多内容都非常合理且合理。一些选择将取决于应用程序的具体情况以及哪个“感觉”正确。在大多数情况下,不会有一个正确的答案。这里有些选择是有意义的,对于下一个应用程序和情况,这些选择可能是完全错误的。在不了解应用程序某些细节的情况下,我认为您在正确的轨道上并已做出一些合理,周到的决定。

对我来说,我觉得主持人几乎应该始终是切入点。使用UI作为切入点会在UI中增加太多逻辑,并且剥夺了在不进行较大代码更改的情况下替换新UI的能力。确实,这是主持人的工作。


也许我应该问的问题是使用MVP的任何方法是完全错误的。我会同意适用于不同情况的变体,但我认为应该有一种公认的方法。MVP的例子都是简单的例子,几乎都需要编辑域对象并保存更改。然而,从具有相同目标的那些示例中,已经产生了以上变化。我刚刚使用视图中引发的事件对应用程序的一部分进行了编码,以在演示者中进行处理,但是我可以让视图持有对演示者的引用并直接调用方法。
JonWillis 2011年

我将尽一切努力简化视图以证明它没有经过单元测试,然后演示者应该有控制权。我对此唯一的担心(在我脑海中可能是错误的)是说winforms / usercontrols可能需要链接到父元素,并且想知道演示者是否需要附加逻辑来编写它。
JonWillis 2011年

@Jon-MVP的方式错了吗?在我的书中,就是视图了解演示者的时间。从某种意义上说,它可能不是“错误的”,但它并不是MVP,在那时它已经变成了其他东西。设计模式的问题在于示例始终尽可能简洁明了。然后,当您第一次实施它们时,现实世界跳了起来,并说:“嘿,真正的应用程序比那个微不足道的示例要复杂得多。” 那就是您开始成长的地方。查找适合您和应用程序情况的内容。
Walter

谢谢你的建议。我记得在大学学习MVC。听起来不错,但是用一块空白的画布,您想知道从哪里开始以及它是如何工作的。关于MVP的错误,我的意思是我在错误的操作方法之上(即,在视图知道演示者如何工作的情况下)发布的MVC的任何想法/变体。还是应该在视图中引用演示者或具体类型等的接口。只有许多不同的变体都可以用于同一目的。
JonWillis 2011年

1
@Jon-您的变化几乎与MVP的精神保持一致。至于使用接口或具体类型,这将取决于应用程序的环境。如果应用程序很小,不是很复杂,那么也许不需要添加界面。我喜欢使事情尽可能简单,直到很明显该应用程序绝对需要实现X体系结构为止。请参阅以下答案以获取更多详细信息:programmers.stackexchange.com/questions/34547/…–
Walter

4

我们在.NET 2.0 Winforms应用程序上使用MVP的修改形式。我们缺少的两部分是WPF ViewModel的修改后的适配器,并添加了数据绑定。我们的特定模式是MVPVM。

在几乎所有情况下,我们都以演示者优先的方式进行连接,但自定义用户控件除外,自定义用户控件以视图优先的方式进行连接以提高设计人员的友好性。我们使用依赖注入,代码生成的ViewModel,用于演示者的BDD和用于模型的TDD / TED。

VM只是一大堆扁平的属性,它们在更改时会引发PropertyChanged。通过代码(及其相关的练习单元测试)生成这些代码非常容易。我们使用它们来读取和写入用户交互控件,以及控制启用状态。ViewModel与View结合使用,因为我们对其他所有事物都使用数据绑定。

视图有时会带有一些方法来完成VM无法完成的任务。通常,这是控制项目(WinForms可能对此挑剔)以及拒绝进行数据绑定的项目的可见性。视图始终公开诸如“登录”或“重新启动”之类的事件,并带有适当的EventArgs来对用户行为进行处理。除非我们必须使用“ View.ShowLoginBox”之类的技巧,否则View可以完全互换,只要它满足一般设计要求即可。

我们花了大约6-8个月的时间来确定这种模式。它有很多部分,但是非常灵活且功能强大。我们的特定实现是非常异步的,并且是事件驱动的,这可能只是其他要求的产物,而不是设计行为的副作用。例如,我向虚拟机继承的基类添加了线程同步(它简单地公开了OnPropertyChanged方法来引发事件)和poof,现在我们可以拥有多线程演示者和模型。


我知道您的MVPVM可能具有商业意义,但如果可以,您可以提供任何示例代码吗?附带说明一下,您在哪里定义模型中的内容以及演示者中的内容。我已经看到演示者是如此基础,他们可以处理视图事件并只调用模型,演示者访问数据层以将业务对象传递给模型,一直到模型被演示者替换。
JonWillis

是的,让我列举一个例子。由于我将其用作内部培训工具,因此将略微针对我们的业务进行量身定制。
Bryan Boettcher

谢谢,我将在周末查看一下,看看是否理解
JonWillis11年

@insta-链接已断开,您可以在某个地方重新上传吗?
Yves Schelpe 2015年

1
废话我刚刚删除它像一个星期前。数字:(
布莱恩·博特彻

2

我正在使用PureMvc的版本,该版本已针对.Net进行了修改,然后由我自己进行了扩展。

我习惯于在Flex应用程序中使用PureMvc。这是一个简单的框架类型,因此如果您要自定义它,它就很容易适应。

我享有以下自由:

  • 使用反射,我能够在视图介体中获取私有属性,以进入表单类并获取对我正在中介的每个控件的(私有)引用。
  • 使用反射,我可以将控件自动连接到调解器中的事件签名,这些签名是通用的,因此我只需打开sender参数即可。
  • 通常,PureMvc希望派生介体在将返回字符串数组的函数中定义其通知兴趣。由于我的兴趣主要是静态的,并且我想有一种更简单的方法来查看调解员的兴趣,因此我进行了修改,以便调解员还可以通过具有特定签名的一组成员变量来声明其兴趣:_ _ _ <classname> _ <notification-name>。这样,我可以看到使用IDE成员树发生的事情,而不是查看函数内部。

在PureMvc中,可以使用Command作为入口点,典型的Start命令将在可能的范围内设置模型,然后创建MainForm,为该表单创建并注册介体,然后执行Application.Run。在表格上。

表单的调解人将负责设置所有子调解人,其中一些可以通过再次使用反射技巧来自动化。

如果我理解您的意思,那么我正在使用的系统是拖放兼容的。实际的表单都是在VS中创建的,但是我的经验仅是包含具有静态创建的控件的表单。通过稍微调整该菜单或子菜单的介体,可以动态地创建菜单项之类的东西。当介体没有静态的根元素可以挂接到并且您开始创建动态的“实例”介体时,它就会变得毛茸茸。

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.