当超越时 RAD (拖放和配置)构建用户界面的方式,许多工具鼓励你可能会遇到三种设计模式 模型 - 视图 - 控制器, 模型 - 视图 - 演示 和 模型 - 视图 - 视图模型。我的问题有三个部分:
- 这些模式解决了哪些问题?
- 它们有什么相似之处?
- 他们有什么不同?
当超越时 RAD (拖放和配置)构建用户界面的方式,许多工具鼓励你可能会遇到三种设计模式 模型 - 视图 - 控制器, 模型 - 视图 - 演示 和 模型 - 视图 - 视图模型。我的问题有三个部分:
在 MVP,Presenter包含View的UI业务逻辑。 View中的所有调用都直接委托给Presenter。 Presenter也直接与View分离,并通过界面与之对话。这是为了允许在单元测试中模拟View。 MVP的一个常见属性是必须进行大量的双向调度。例如,当有人单击“保存”按钮时,事件处理程序将委托给Presenter的“OnSave”方法。保存完成后,Presenter将通过其界面回调View,以便View可以显示保存已完成。
MVP往往是在Web窗体中实现单独呈现的非常自然的模式。原因是View总是首先由ASP.NET运行时创建。您可以 了解有关两种变体的更多信息。
被动观点: 视图尽可能愚蠢,几乎没有逻辑。演示者是一个与视图和模型对话的中间人。视图和模型完全相互屏蔽。模型可能会引发事件,但Presenter会订阅它们以更新视图。在Passive View中没有直接数据绑定,而View公开了Presenter用于设置数据的setter属性。所有状态都在Presenter中管理,而不是View。
监督控制员: Presenter处理用户手势。 View直接通过数据绑定绑定到Model。在这种情况下,Presenter的工作是将Model传递给View,以便它可以绑定到它。 Presenter还将包含手势逻辑,如按下按钮,导航等。
在里面 MVC,Controller负责确定响应任何操作(包括应用程序加载时)显示的视图。这与MVP不同,其中操作通过View路由到Presenter。在MVC中,View中的每个操作都与对Controller的调用以及操作相关联。在网络中,每个动作都涉及对URL的调用,在URL的另一侧有一个Controller响应。控制器完成处理后,将返回正确的视图。在整个应用程序的生命周期中,序列以这种方式继续:
视图中的操作 - >致电控制器 - >控制器逻辑 - > Controller返回View。
MVC的另一个重要区别是View不直接绑定到Model。视图简单地呈现,并且完全是无状态的。在MVC的实现中,View通常在后面的代码中没有任何逻辑。这与MVP完全相反,因为如果View没有委托给Presenter,它将永远不会被调用。
另一种要看的模式是 演示模型 模式。在这种模式中没有Presenter。相反,View直接绑定到Presentation Model。 Presentation Model是专为View设计的模型。这意味着该模型可以公开一个永远不会放在域模型上的属性,因为它会违反关注点分离。在这种情况下,表示模型绑定到域模型,并可以订阅来自该模型的事件。 View然后订阅来自Presentation Model的事件并相应地更新自身。 Presentation Model可以公开视图用于调用操作的命令。这种方法的优点在于,您可以完全删除代码隐藏,因为PM完全封装了视图的所有行为。这种模式非常适合在WPF应用程序中使用,也称为 模型 - 视图 - 视图模型。
有一个 关于Presentation Model的MSDN文章 和一节 WPF复合应用指南 (前棱镜)讲述 分离的演示模式
我曾经在博客上写过这篇文章 Todd Snyder关于两者之间差异的优秀帖子:
以下是两者之间的主要区别 模式:
MVP模式
- 视图与模型更加松散耦合。主持人是 负责将模型绑定到 风景。
- 更容易进行单元测试,因为与视图的交互是通过 一个界面
- 通常一对一地查看演示者地图。复杂的观点可能有 多位主持人。
MVC模式
- 控制器基于行为,可以跨越共享 意见
- 可以负责确定要显示的视图
这是我能找到的网上最好的解释。
这是对这些设计模式的许多变体的过度简化,但这就是我喜欢考虑两者之间差异的方式。
MVC
MVP
以下是代表通信流程的插图
MVP是 不 必然是View负责的场景(例如,参见Taligent的MVP)。
我发现不幸的是,人们仍在宣传这种模式(负责观点),而不是反模式,因为它与“它只是一种观点”(实用程序员)相矛盾。 “这只是一种观点”指出向用户显示的最终视图是应用程序的次要问题。微软的MVP模式使得重复使用Views变得更加困难 便利地 借助微软的设计师鼓励不良做法。
坦率地说,我认为MVC的基本关注对任何MVP实现都适用,差异几乎完全是语义的。只要您关注视图(显示数据),控制器(初始化和控制用户交互)和模型(基础数据和/或服务)之间的关注点分离,那么您就可以实现MVC的优势。如果您正在获得好处,那么谁真正关心您的模式是MVC,MVP还是监督控制器?唯一的 真实 模式仍然是MVC,其余的只是不同的风格。
考虑 这个 非常令人兴奋的文章,全面列出了许多这些不同的实现。 您可能会注意到,他们基本上都在做同样的事情,但略有不同。
我个人认为MVP最近才被重新引入作为一个吸引人的术语,要么减少语义偏执者之间的争论,他们争论某些东西是否真的是MVC,或者证明微软快速应用程序开发工具的合理性。在我的书中,这些原因都不能证明它作为一种单独的设计模式存在。
在大多数情况下,视图会创建其演示者。演示者将与模型交互并通过界面操纵视图。视图有时会与演示者交互,通常通过某种界面进行交互。这归结为实施;您希望视图在演示者上调用方法还是希望视图具有演示者侦听的事件?它归结为:视图了解演示者。视图委托给演示者。
根据某些事件/请求创建或访问控制器。然后,控制器创建适当的视图并与模型交互以进一步配置视图。它归结为:控制器创建和管理视图;视图是控制器的从属。视图不知道控制器。
MVC(模型视图控制器)
输入首先指向Controller,而不是视图。该输入可能来自与页面交互的用户,但也可能只是将特定URL输入到浏览器中。在任何一种情况下,它都是一个与之连接的控制器,以启动一些功能。 Controller和View之间存在多对一关系。这是因为单个控制器可以基于正在执行的操作选择要呈现的不同视图。 请注意从Controller到View的单向箭头。这是因为View对控制器没有任何了解或参考。 Controller确实传回了Model,因此View和传递给它的预期模型之间存在知识,但是不是Controller提供它。
MVP(模型视图演示者)
输入以View开头,而不是Presenter。 View和关联的Presenter之间存在一对一映射。 View包含对Presenter的引用。 Presenter也对从View触发的事件做出反应,因此它知道与之关联的View。 Presenter根据它在Model上执行的请求操作更新View,但View不支持Model。
更多 参考
MVC =模型 - 视图 - 控制器
另外值得记住的是,MVP也有不同类型。福勒已经将模式分解为两个 - 被动视图和监督控制器。
使用被动视图时,View通常会实现细粒度的界面,其属性或多或少地直接映射到底层UI小部件。例如,您可能拥有一个ICustomerView,其中包含Name和Address等属性。
您的实现可能如下所示:
public class CustomerView : ICustomerView
{
public string Name
{
get { return txtName.Text; }
set { txtName.Text = value; }
}
}
您的Presenter类将与模型对话并将其“映射”到视图中。这种方法称为“被动视图”。好处是视图易于测试,并且在UI平台(Web,Windows / XAML等)之间移动更容易。缺点是你不能利用像数据绑定这样的东西 真 像框架一样强大 WPF 和 Silverlight的)。
MVP的第二种风格是监督控制器。在这种情况下,您的View可能有一个名为Customer的属性,然后再将其数据绑定到UI小部件。您不必考虑同步和微观管理视图,监督控制器可以在需要时介入并提供帮助,例如使用编译的交互逻辑。
MVP的第三个“风味”(或者有人可能称之为单独的模式)是Presentation Model(或者有时称为Model-View-ViewModel)。与MVP相比,您将M和P“合并”为一个类。您拥有UI小部件所绑定的客户对象,但您还有其他UI特定字段,如“IsButtonEnabled”或“IsReadOnly”等。
我认为我在UI架构中找到的最好的资源是Jeremy Miller所做的一系列博客文章 构建自己的CAB系列目录。他涵盖了MVP的所有风格,并展示了C#代码来实现它们。
我还在Silverlight over的上下文中写了关于Model-View-ViewModel模式的博客 YouCard重新访问:实现ViewModel模式。
问题有很多答案,但我觉得需要一些非常简单的答案,清楚地比较两者。以下是用户在MVP和MVC应用中搜索电影名称时所做的讨论:
用户:点击点击...
视图: 那是谁? [MVP|MVC]
用户:我刚点击了搜索按钮......
视图:好的,等一下...... [MVP|MVC]
( 视图 打电话给 主持人|调节器 ......)[MVP|MVC]
视图:嘿 主持人|调节器,用户刚刚点击了搜索按钮,我该怎么办? [MVP|MVC]
主持人|调节器:嘿 视图,该页面上是否有搜索字词? [MVP|MVC]
视图:是的,......这里是......“钢琴”[MVP|MVC]
主持人: 谢谢 视图,...同时我正在查找搜索词 模型,请告诉他/她一个进度条[MVP|MVC]
( 主持人|调节器 正在呼唤 模型 ......)[MVP|MVC]
主持人|调节器:嘿 模型,你对这个搜索词有什么匹配吗?:“piano”[MVP|MVC]
模型:嘿 主持人|调节器,让我查一下...... [MVP|MVC]
( 模型 正在查询电影数据库......)[MVP|MVC]
( 过了一会儿 ... )
--------------这是MVP和MVC开始分歧的地方---------------
模型:我找到了一份清单, 主持人,这里是JSON“[{”name“:”Piano Teacher“,”year“:2001},{”name“:”Piano“,”year“:1993}]”[MVP]
模型:有一些结果可用, 调节器。我在我的实例中创建了一个字段变量,并用结果填充它。它的名字是“searchResultsList”[MVC]
(主持人|调节器 谢谢 模型 然后回到 视图)[MVP|MVC]
主持人:谢谢你的等待 视图,我找到了一份匹配结果列表,并以一种可呈现的格式排列:[“钢琴老师2001”,“钢琴1993”]。请在垂直列表中向用户显示。也请现在隐藏进度条[MVP]
调节器:谢谢你的等待 视图, 我问过了 模型 关于您的搜索查询。它说它找到了一个匹配结果列表,并将它们存储在其实例中名为“searchResultsList”的变量中。你可以从那里得到它。也请现在隐藏进度条[MVC]
视图: 非常感谢你 主持人 [MVP]
视图:谢谢你“控制器”[MVC] (现在 视图 质疑自己:我应该如何呈现我从中得到的结果 模型 对用户?电影的制作年份应该是第一个还是最后一个......?它应该是垂直还是水平列表? ...)
如果您有兴趣,我一直在撰写一系列文章,涉及应用程序架构模式(MVC,MVP,MVVP,清洁架构......)以及Github回购 这里。即使样本是为Android编写的,基本原则也可以应用于任何媒介。