当超越许多工具鼓励的RAD(拖放和配置)构建用户界面的方式时,您可能会遇到三种设计模式,称为模型-视图-控制器、模型-视图-展示器和模型-视图-视图模型。我的问题分为三个部分:
这两个框架都旨在分离关注点——例如,与数据源(模型)的交互、应用程序逻辑(或将这些数据转换为有用的信息)(控制器/演示器)和显示代码(视图)。在某些情况下,模型也可以用来将数据源转换为更高级别的抽象。一个很好的例子是MVC店面项目。
有一个关于MVC与MVP之间差异的讨论这里。
区别在于,在MVC应用程序中,传统上视图和控制器与模型交互,但彼此不交互。
MVP设计让Presenter访问模型并与视图交互。
话虽如此,ASP.NETMVC是一个MVP框架,因为控制器访问模型来填充视图,这意味着没有逻辑(只是显示控制器提供的变量)。
要了解MVC与MVP的ASP.NET区别,请查看Scott Hanselman的此MIX演示文稿。
我写了一篇博客,引用托德·斯奈德关于两者差异的精彩文章:
以下是两者之间的主要区别模式: MVP模式 视图与模型的耦合更加松散。演示者是负责将模型绑定到的观点。更容易进行单元测试,因为与视图的交互是通过一个接口通常视图到演示者映射为一对一。复杂的视图可能有多个演示者。 MVC模式 控制器基于行为,可以在查看次数可以负责确定要显示的视图
以下是两者之间的主要区别模式:
MVP模式
MVC模式
这是我在网上能找到的最好的解释。
MVC=模型-视图-控制器
在大多数情况下,视图创建它的演示者。演示者将与模型交互并通过接口操作视图。视图有时会与演示者交互,通常是通过一些接口。这归结为实现;你希望视图调用演示者的方法,还是希望视图具有演示者监听的事件?归结为:视图知道演示者。视图委托给演示者。
基于某些事件/请求创建或访问控制器。然后控制器创建适当的视图并与模型交互以进一步配置视图。归结为:控制器创建和管理视图;视图是控制器的奴隶。视图不知道控制器。
同样值得记住的是,也有不同类型的MVP。Fowler将模式分为两种——被动视图和监督控制器。
使用被动视图时,您的视图通常实现一个细粒度界面,其属性或多或少直接映射到底层UI小部件。例如,您可能有一个带有名称和地址等属性的ICustmerView。
你的实现可能看起来像这样:
public class CustomerView : ICustomerView{public string Name{get { return txtName.Text; }set { txtName.Text = value; }}}
你的Presenter类将与模型对话并将其“映射”到视图。这种方法称为“被动视图”。好处是视图易于测试,并且更容易在UI平台(Web、Windows/XAML等)之间移动。缺点是你不能利用像数据绑定这样的东西(在WPF和Silverlight等框架中真的强大)。
MVP的第二种风格是监督控制器。在这种情况下,你的视图可能有一个名为客户的属性,它再次是UI小部件的数据库。你不必考虑同步和微观管理视图,监督控制器可以在需要时介入并提供帮助,例如使用完整的交互逻辑。
MVP(或者有人可能会称之为单独的模式)的第三种“味道”是演示模型(或有时称为模型-视图-视图模型)。与MVP相比,你将M和P“合并”到一个类中。你有你的UI小部件数据绑定到的客户对象,但你也有额外的UI规范字段,如“IsButtonEn的”或“IsReadOnly”等。
我认为我找到的关于UI架构的最好的资源是Jeremy Miller在建立自己的CAB系列目录上写的一系列博客文章。他涵盖了MVP的所有风格,并展示了实现它们的C#代码。
我还在YouCard重新访问:实现ViewModel模式的Silverlight上下文中写了关于Model-View-ViewModel模式的博客。
MVP没有必须是View负责的场景(例如,请参阅Taligent的MVP)。我觉得很不幸的是,人们仍然将其作为一种模式(View负责)而不是反模式来宣扬,因为它与“这只是一个视图”(务实的程序员)相矛盾。“这只是一个视图”指出,向用户显示的最终视图是应用程序的次要关注点。微软的MVP模式使Views的重用变得更加困难,方便也为微软的设计师鼓励不良做法提供了借口。
坦率地说,我认为MVC的潜在关注点适用于任何MVP实现,差异几乎完全是语义上的。只要你遵循视图(显示数据)、控制器(初始化和控制用户交互)和模型(底层数据和/或服务)之间的关注点分离,那么你就实现了MVC的好处。如果你正在实现好处,那么谁真正关心你的模式是MVC、MVP还是Supervising Controller?唯一的0模式仍然是MVC,其余的只是它的不同味道。
考虑这个这篇非常令人兴奋的文章,它全面列出了许多不同的实现。你可能会注意到它们基本上都在做同样的事情,但略有不同。
我个人认为MVP只是最近才作为一个吸引人的术语被重新引入,以减少语义偏执狂之间争论某事是否真正是MVC的争论,或者证明微软的快速应用程序开发工具是合理的。
在MVP中,演示者包含视图的UI业务逻辑。来自视图的所有调用都直接委托给演示者。演示者也直接与视图解耦,并通过接口与它对话。这是为了允许在单元测试中模拟视图。MVP的一个常见属性是必须有很多双向调度。例如,当有人单击“保存”按钮时,事件处理程序将委托给演示者的“OnSave”方法。保存完成后,演示者将通过其接口回调View,以便View可以显示保存已完成。
MVP往往是在WebForms中实现分离表示的一种非常自然的模式。原因是视图总是首先由ASP.NET运行时创建。您可以了解更多关于这两种变体。
被动视图:视图尽可能愚蠢,几乎没有逻辑。演示者是与视图和模型交谈的中间人。视图和模型完全相互屏蔽。模型可以引发事件,但演示者订阅它们以更新视图。在被动视图中,没有直接的数据绑定,相反,视图公开了演示者用来设置数据的setter属性。所有状态都在演示者而不是视图中管理。
监督控制员:演示者处理用户手势。视图通过数据绑定直接绑定到模型。在这种情况下,演示者的工作是将模型传递给视图,以便它可以绑定到它。演示者还将包含按下按钮、导航等手势的逻辑。
在MVC中,控制器负责确定响应任何操作(包括应用程序加载时)显示哪个视图。这与MVP不同,MVP中的操作通过视图路由到演示者。在MVC中,视图中的每个操作都与对控制器的调用以及操作相关联。在网络中,每个操作都涉及对另一边的URL的调用,控制器会做出响应。一旦控制器完成处理,它将返回正确的视图。在应用程序的整个生命周期中,序列以这种方式继续:
Action in the View-> Call to Controller-> Controller Logic-> Controller returns the View.
MVC的另一个很大的区别是视图不直接绑定到模型。视图只是渲染并且完全无状态。在MVC的实现中,视图通常不会在后面的代码中有任何逻辑。这与MVP相反,因为如果视图不委托给演示者,它永远不会被调用。
另一个要查看的模式是演示模型模式。在此模式中,没有演示者。相反,视图直接绑定到演示模型。演示模型是专门为视图构建的模型。这意味着此模型可以公开永远不会放在域模型上的属性,因为这违反了关注点分离。在这种情况下,演示模型绑定到域模型,并可以订阅来自该模型的事件。然后视图订阅来自演示模型的事件并相应地更新自己。演示模型可以公开视图用于调用操作的命令。这种方法的优点是,您基本上可以完全删除代码隐藏,因为PM完全封装了视图的所有行为。这种模式是WPF应用程序中非常强大的候选者,也称为模型-视图-视图模型。
有一个关于表示模型的MSDN文章和一个部分在WPF复合应用指南(前棱镜)关于分离的表示模式
两者都是试图分离表示和业务逻辑的模式,将业务逻辑与UI方面分离
在架构上,MVP是基于页面控制器的方法,而MVC是基于前端控制器的方法。这意味着在MVP标准的Web表单页面生命周期中,只是通过从后面的代码中提取业务逻辑来增强。换句话说,页面是服务超文本传输协议请求的一个。换句话说,MVP IMHO是Web表单增强的进化类型。另一方面,MVC完全改变了游戏,因为请求在页面加载之前被控制器类拦截,业务逻辑在那里执行,然后在控制器处理的最终结果中,数据刚刚转储到页面(“视图”)从这个意义上说,MVC看起来(至少对我来说)与路由引擎增强的MVP的Supervising Controller风格有很多不同。
它们都支持TDD,并且有缺点和缺点。
关于如何选择其中一个IMHO的决定应该基于一个人在ASP NET Web表单类型的Web开发中投入了多少时间。如果有人认为自己擅长网络表单,我会推荐MVP。如果在页面生命周期等方面感到不舒服,MVC可能是一种方式。
这是另一个博客文章链接,提供了有关此主题的更多细节
http://blog.vuscode.com/malovicn/archive/2007/12/18/model-view-presenter-mvp-vs-model-view-controller-mvc.aspx
我使用过MVP和MVC,尽管我们作为开发人员倾向于关注这两种模式的技术差异,但IMHO中MVP的重点更多地与易于采用有关。
如果我在一个已经有良好的Web表单开发风格背景的团队中工作,那么引入MVP比MVC要容易得多。我想说,在这种情况下,MVP是一个快速的胜利。
我的经验告诉我,将团队从Web表单迁移到MVP,然后从MVP迁移到MVC相对容易;从Web表单迁移到MVC更困难。
我在这里留下了我的一个朋友发表的关于MVP和MVC的一系列文章的链接。
http://www.qsoft.be/post/Building-the-MVP-StoreFront-Gutthrie-style.aspx
简而言之,MVP是面向大尺度的,MVC是面向小尺度的。用MVC的时候,感觉V和C是一个不可分割的整体的两面,是和M直接绑定在一起的。如果是小尺度的,比如UI控件,基础小部件,都会陷入这种状态。在这种颗粒度的场景,MVP就没什么意义了。当规模变大的时候,合理的接口就更重要了,明确的职责分工也是如此,MVP就出现了。
另一方面,当平台特性倾向于组件之间的某种关系时,这种经验法则的权重可能很小,就像Web一样,在那里实现MVC似乎比MVP更容易。
在MVP中,视图从演示者那里提取数据,演示者从模型中提取数据并准备/规范化数据,而在MVC中,控制器从模型中提取数据并通过在视图中推送进行设置。
在MVP中,您可以让单个视图与多种类型的演示者一起工作,让单个演示者与不同的多个视图一起工作。
MVP通常使用某种绑定框架,例如Microsoft WPF绑定框架或用于HTML5和Java的各种绑定框架。
在这些框架中,UI/HTML5/XAML知道每个UI元素显示的演示者的属性,因此当您将视图绑定到演示者时,视图会查找属性并知道如何从它们中提取数据以及当用户在UI中更改值时如何设置它们。
因此,例如,如果模型是一辆汽车,则演示者是某种汽车演示者,向视图公开汽车属性(年份、制造商、座位等)。视图知道名为“汽车制造商”的文本字段需要显示演示者制造商属性。
然后,您可以将许多不同类型的演示者绑定到视图,所有这些都必须具有Maker属性-它可以是飞机,火车或任何东西,视图都不在乎。视图从演示者中提取数据-无论哪一个-只要它实现了商定的接口。
这个绑定框架,如果你把它去掉,它实际上是控制器:-)
因此,您可以将MVP视为MVC的演变。
MVC很棒,但问题是通常它的控制器每个视图。控制器A知道如何设置视图A的字段。如果现在,你想视图A显示模型B的数据,你需要控制器A知道模型B,或者你需要控制器A接收一个带有接口的对象-这就像没有绑定的MVP,或者你需要在控制器B中重写UI集代码。
结论-MVP和MVC都是UI模式的解耦,但MVP通常使用下面是MVC的绑定框架。THUS MVP处于比MVC更高的架构级别,在MVC之上是包装模式。
这是对这些设计模式的许多变体的过度简化,但这是我喜欢思考两者之间差异的方式。
MVC
MVP
以下是表示通信流的插图
MVC有很多版本,这个答案是关于Smalltalk中的原始MVC。简而言之,它是
这个演讲droidcon NYC 2017-使用架构组件进行清洁应用程序设计澄清了它
MVC(模型视图控制器)
输入首先指向控制器,而不是视图。该输入可能来自与页面交互的用户,但也可能来自简单地将特定URL输入浏览器。在任何一种情况下,它都是与控制器交互以启动某些功能的控制器。控制器和视图之间存在多对一的关系。这是因为单个控制器可以根据正在执行的操作选择不同的视图来呈现。注意从Controller到View的单向箭头。这是因为View不了解或引用控制器。控制器确实传回了模型,所以视图和传递给它的预期模型之间有知识,但控制器没有提供它。
MVP(模型-视图-演示者)
输入以View开头,而不是Presenter。View和关联的Presenter之间存在一对一映射。视图保存对演示者的引用。演示者还对从视图触发的事件做出反应,因此它知道与其关联的视图。Presenter根据它对Model执行的请求操作更新View,但View不知道Model。
更多参考
MVP代表模型-视图-演示器。这在2007年初出现,当时微软推出了Smart Client Windows应用程序。
演示者在MVP中充当监督角色,从模型绑定视图事件和业务逻辑。
视图事件绑定将从视图界面在Presenter中实现。
视图是用户输入的发起者,然后将事件委托给演示者,演示者处理事件绑定并从模型中获取数据。
优点:视图只有UI,没有任何逻辑可测试性高
<强>缺点:实现事件绑定时有点复杂和更多的工作
MVC代表模型-视图-控制器。控制器负责创建模型和使用绑定模型呈现视图。
控制器是发起者,它决定呈现哪个视图。
优点:强调单一责任原则可测试性高
<强>缺点:如果尝试在同一个控制器中呈现多个视图,有时控制器的工作量太大。
最简单的答案是视图如何与模型交互。在MVP中,视图由演示者更新,演示者充当视图和模型之间的中介。演示者从视图中获取输入,视图从模型中检索数据,然后执行所需的任何业务逻辑,然后更新视图。在MVC中,模型直接更新视图,而不是通过控制器返回。
模型-视图-控制器
MVC是软件应用程序架构的模式。它将应用程序逻辑分为三个独立的部分,促进模块化、易于协作和重用。它还使应用程序更加灵活,欢迎迭代。
为了让这一点更清楚,让我们想象一个简单的购物清单应用程序。我们想要的只是本周需要购买的每件商品的名称、数量和价格列表。下面我们将描述如何使用MVC实现其中的一些功能。
模型-视图-展示器
如果您想查看简单实现的示例,请检查这个 GitHub帖子
从数据库查询和显示用户列表的具体工作流程可以如下工作:
MVC和MVP模式之间的差异是什么?
控制器基于行为,可以在视图之间共享
可以负责确定显示哪个视图(Front Controller Pattern)
视图更松散地耦合到模型。演示者负责将模型绑定到视图。
更容易进行单元测试,因为与视图的交互是通过接口进行的
通常视图到演示者映射为一对一。复杂的视图可能有多个演示者。
鲍勃叔叔有这个个不错的视频,他在最后简要解释了MVC和MVP。
在我看来,MVP是MVC的改进版本,你基本上将要显示的内容(数据)与如何显示(视图)的关注分开。演示者包括你的UI的业务逻辑,隐式强加应该呈现的数据,并给你一个哑视图模型列表。当显示数据的时候,你只需将你的视图(可能包括相同的id)插入你的适配器,并使用这些视图模型设置相关的视图字段,引入的代码量最少(只是使用setter)。它的主要好处是您可以针对许多/各种视图测试您的UI业务逻辑,例如在水平列表或垂直列表中显示项目。
在MVC中,我们通过接口(边界)来粘合不同的层。控制器是我们架构的插件,但它没有这样的限制来强加显示什么。从这个意义上说,MVP是一种MVC,其概念是视图可以通过适配器插入控制器。
我希望这有助于更好。
这个问题有很多答案,但我觉得需要一些非常简单的答案来清楚地比较两者。这是当用户在MVP和MVC应用程序中搜索电影名称时我组成的讨论:
用户:点击点击…
查看:那是谁?[MVP|MVC]
用户:我刚刚点击了搜索按钮…
查看:好的,等一下 … . [MVP|MVC]
(查看呼叫演示者|控制器 … ) [MVP|MVC]
查看:嘿演示者|控制器,用户刚刚点击了搜索按钮,我该怎么办?[MVP|MVC]
演示者|控制器:嘿查看,那个页面上有搜索词吗?[MVP|MVC]
查看:是的,…在这里…"钢琴"[MVP|MVC]
演示者|控制器:谢谢查看,…同时我正在查找模型上的搜索词,请给他/她一个进度条[MVP|MVC]
(演示者|控制器正在呼叫模型 … ) [MVP|MVC]
:嘿|,让我查一下…[MVP|MVC]
(模型正在查询电影数据库 … ) [MVP|MVC]
(过了一会儿……)
-------------- 这就是MVP和MVC开始分歧的地方 ---------------
模型:我找到了一个列表给你,演示者,这里是JSON"[{"name":"Piano老师","年份": 2001},{"name":"Piano","年份": 1993}]"[MVP]
模型:有一些可用的结果,控制器。我在我的实例中创建了一个字段变量并用结果填充了它。它的名字是“搜索结果列表”[MVC]
(演示者|控制器谢谢模型,回到查看)[MVP|MVC]
演示者:感谢您的等待查看,我为您找到了一个匹配结果列表,并将它们排列成一个可展示的格式:[“钢琴老师2001”,“钢琴1993”]。请以垂直列表的形式显示给用户。也请现在隐藏进度条[MVP]
控制器:感谢您的等待查看,我已经向模型询问了您的搜索查询。它说它找到了一个匹配结果的列表,并将它们存储在其实例内名为“搜索结果列表”的变量中。您可以从那里获取它。还请现在隐藏进度条[MVC]
查看:非常感谢演示者[MVP]
查看:谢谢控制器[MVC](现在查看正在质疑自己:我应该如何将从模型获得的结果呈现给用户?电影的制作年份应该排在第一位还是最后……?它应该在垂直列表还是水平列表中?…)
如果你感兴趣,我一直在写一系列关于应用程序架构模式(MVC、MVP、MVVP、干净架构……)的文章,并附有Github repo这里。尽管该示例是为android编写的,但基本原则可以应用于任何媒体。
我认为Erwin Vandervalk的这张图片(以及随附的文章)是对MVC、MVP和MVVM及其相似性和差异的最佳解释。文章没有出现在搜索引擎查询“MVC、MVP和MVVM”的结果中,因为文章的标题不包含“MVC”和“MVP”这两个词;但我认为这是最好的解释。
(文章也符合Bob Martin叔叔在他的一次演讲中所说的:MVC最初是为小型UI组件设计的,而不是为系统的架构设计的)
几句话,
你忘记了动作-域-响应者(ADR)。
正如上面的一些图形所解释的,MVC中模型和查看之间存在直接关系/链接。在控制器上执行一个操作,它将在模型上执行一个操作。模型中的那个动作,查看中的会引发反应。查看总是在模型的状态更改时更新。
有些人总是忘记,MVC创建于70年代末”和Web是在80年代末/90年代初创建的。MVC最初不是为Web创建的,而是为桌面应用程序创建的,其中控制器、模型和视图将共存。
因为我们使用的Web框架(eg:. laravel)仍然使用相同的命名约定(模型-视图-控制器),我们倾向于认为它一定是MVC,但它实际上是别的东西。
让我们来看看动作-域-响应者。在ADR中,控制器得到一个行动,它将在型号/领域中执行一个操作。到目前为止,相同。不同的是,它然后收集该操作的响应/数据,并将其传递给响应者(eg:.#0)进行渲染。当在同一组件上请求新操作时,再次调用控制器,循环重复。在ADR中,模型/域和视图之间有没有联系(回应者的回应)。
备注: Wikipedia声明“但是,每个ADR操作都由单独的类或闭包表示。”。这是没有必然正确的。多个操作可以在同一个Controller中,并且模式仍然相同。
动作-域-响应器9;mvc&动作-域-响应器9;" rel="tag">mvc动作-域-响应器9;adr&动作-域-响应器9;" rel="tag">adr动作-域-响应器9;模型-视图-控制器&动作-域-响应器9;" rel="tag">模型-视图-控制器动作-域-响应器
在MVC中,控制器是负责人!控制器根据一些事件/请求被触发或访问,然后管理视图。
MVC中的视图实际上是无状态的,控制器负责选择要显示的视图。
例如:当用户单击“Show MyProfile”按钮时,控制器被触发。它与模型通信以获取适当的数据。然后,它显示一个类似于配置文件页面的新视图。控制器可以从模型中获取数据并将其直接提供给视图——如上图所示——或者让视图自己从模型中获取数据。
在MVP中,视图是负责人!每个视图调用其Presenter或具有Presenter侦听的一些事件。
MVP中的视图不实现任何逻辑,Presenter负责实现所有逻辑并使用某种接口与View通信。
例如:当用户单击“保存”按钮时,View中的事件处理程序将委托给Presenter的“OnSave”方法。Presenter将执行所需的逻辑和与Model的任何必要通信,然后通过其接口回调View,以便View可以显示保存已完成。