当我们超越许多工具鼓励的RAD(拖放和配置)方式构建用户界面时,您可能会遇到三种设计模式,称为Model-View-Controller, Model-View-Presenter 和 Model-View-ViewModel。我的问题分为三个部分:
- 这些模式解决了哪些问题?
- 它们有什么相似之处?
- 它们有什么不同之处?
当我们超越许多工具鼓励的RAD(拖放和配置)方式构建用户界面时,您可能会遇到三种设计模式,称为Model-View-Controller, Model-View-Presenter 和 Model-View-ViewModel。我的问题分为三个部分:
在MVP中,Presenter包含了视图的UI业务逻辑。所有来自视图的调用都直接委托给Presenter。Presenter也直接与视图解耦,并通过接口与其通信。这是为了允许在单元测试中对视图进行模拟。MVP的一个常见特征是必须有大量的双向分派。例如,当有人点击“保存”按钮时,事件处理程序将委托给Presenter的"OnSave"方法。一旦保存完成,Presenter将通过其接口回调视图,以便视图可以显示保存已完成。
MVP往往是在WebForms中实现分离呈现的一种非常自然的模式。原因是视图始终是由ASP.NET运行时首先创建的。您可以find out more about both variants。
被动视图:视图尽可能地愚笨,几乎不包含任何逻辑。Presenter是一个中间人,负责与视图和模型交互。视图和模型完全隔离。模型可能会引发事件,但Presenter订阅它们以更新视图。在被动视图中,没有直接的数据绑定,相反,视图公开setter属性,Presenter使用这些属性设置数据。所有状态都由Presenter管理,而不是视图。
监控控制器:Presenter处理用户手势。视图通过数据绑定直接绑定到模型。在这种情况下,Presenter的工作是将模型传递给视图,以便它可以与之绑定。Presenter还将包含有关手势(如按按钮,导航等)的逻辑。
在MVC中,控制器负责确定响应任何操作(包括应用程序加载时)显示哪个视图。这与MVP不同,MVP中的操作通过视图路由到Presenter。在MVC中,视图中的每个操作都与对控制器的调用及其操作相关联。在Web中,每个操作涉及对另一侧URL的调用,在该URL的另一侧有一个响应的控制器。一旦该控制器完成其处理,它将返回正确的视图。该序列在应用程序的整个生命周期中继续进行:
视图中的操作 -> 调用控制器 -> 控制器逻辑 -> 控制器返回视图。
MVC的另一个重要区别是View不直接绑定到Model。View只是简单地渲染,完全没有状态。在MVC的实现中,View通常不会在代码后面有任何逻辑。这与MVP相反,因为如果View不委托给Presenter,它将永远不会被调用。
另一个要看的模式是展示模型模式。在这个模式中,没有Presenter。相反,View直接绑定到展示模型。展示模型是专门为View设计的Model。这意味着该模型可以公开一些您永远不会放在域模型上的属性,因为这将违反关注点分离原则。在这种情况下,展示模型绑定到域模型,并可能订阅来自该模型的事件。然后,View订阅来自展示模型的事件,并相应地更新自己。展示模型可以公开命令,View用于调用操作。这种方法的优点是您可以完全删除代码后面,因为PM完全封装了View的所有行为。此模式非常适合用于WPF应用程序,并称为Model-View-ViewModel。
这只是这些设计模式的许多变体的简化,但这是我喜欢思考两者之间差异的方式。
MVC
MVP
这是我能找到的最好的解释。以下是模式之间的主要区别:
MVP模式
- 视图与模型之间的耦合性较弱。Presenter负责将模型与视图绑定。
- 易于单元测试,因为与视图的交互是通过接口进行的。
- 通常视图到Presenter呈一对一映射。复杂视图可能有多个Presenter。
MVC模式
- 控制器基于行为,可以在多个视图中共享。
- 可以负责确定显示哪个视图。
MVP并不一定是View负责的场景(例如Taligent的MVP)。我认为仍有人把这作为一种模式(View负责)而非反模式,这与“它只是一个视图”(Pragmatic Programmer)相矛盾。 “它只是一个视图”表示向用户显示的最终视图是应用程序的次要关注点。Microsoft的MVP模式使得视图的重复使用更加困难,并方便地为Microsoft的设计师辩护,以免鼓励不良实践。
坦白地说,我认为MVC的基本问题对于任何MVP实现都是正确的,差异几乎完全是语义上的。只要在视图(显示数据)、控制器(初始化和控制用户交互)和模型(底层数据和/或服务)之间遵循关注点分离,就可以实现MVC的好处。如果您正在实现这些好处,则无论您的模式是MVC、MVP还是Supervising Controller,都没有太大关系。唯一真正的模式仍然是MVC,其余只是不同的风味。
请查看此非常令人兴奋的文章,其中详细列出了许多这些不同的实现。您可能会注意到它们基本上都在以稍微不同的方式做着相同的事情。
我个人认为,MVP只是最近作为一个流行术语重新引入,以减少那些争论某些东西是否真正符合MVC标准的人之间的争吵,或者为Microsoft的快速应用程序开发工具辩护。在我看来,这两个原因都不能证明MVP作为一种单独的设计模式的存在。
在大多数情况下,视图会创建其自身的 presenter。Presenter 将与模型交互,并通过接口操作视图。视图有时会通过接口与 presenter 进行交互,通常情况下是这样的。具体实现上,你是想让视图调用 presenter 的方法呢,还是想让视图拥有 presenter 监听的事件?总之就是这样:视图知道 presenter 的存在,因此视图委托给了 presenter。
基于某些事件/请求,控制器被创建或访问。然后,控制器创建适当的视图,并与模型交互以进一步配置视图。简而言之,控制器创建和管理视图;视图受控制器支配。视图不知道控制器的存在。
MVC (模型-视图-控制器)
输入先经过控制器,而不是视图。这个输入可能来自用户与页面交互,但也可能只是在浏览器中输入特定的 URL。无论哪种情况,都是通过控制器接口启动某些功能。
控制器与视图之间存在多对一的关系。这是因为单个控制器可以选择不同的视图进行渲染,具体取决于正在执行的操作。
请注意从控制器到视图的单向箭头。这是因为视图没有任何控制器的知识或引用。
控制器确实会传回模型,因此视图了解并期望接收到的模型,但不了解提供它的控制器。
MVP(模型-视图-表示器)
输入始于视图,而不是表示器。
视图与相关表示器之间有一对一的映射关系。
视图保存对表示器的引用。表示器还会响应从视图触发的事件,因此它知道与其相关联的视图。
表示器根据其对模型执行的请求操作更新视图,但视图不了解模型。
更多参考:http://geekswithblogs.net/dlussier/archive/2009/11/21/136454.aspx
对于这个问题,有很多答案,但我认为需要一些真正简单的答案来清晰比较这两种模式。以下是当用户在MVP和MVC应用程序中搜索电影名称时,我编写的讨论:
用户:点击点击...
视图:谁啊?[MVP|MVC]
用户:我刚才点击了搜索按钮…
视图:好的,请稍等...[MVP|MVC]
(视图调用Presenter|Controller...) [MVP|MVC]
视图:嘿 Presenter|Controller,一个用户刚刚点击了搜索按钮,我该怎么办?[MVP|MVC]
Presenter|Controller:嘿 View,这个页面上有任何搜索术语吗?[MVP|MVC]
视图:有,“piano”就在这里[MVP|MVC]
Presenter|Controller:谢谢View,...与此同时,我正在模型上查找搜索术语,请向用户显示进度条[MVP|MVC]
( Presenter|Controller在调用Model...) [MVP|MVC]
Presenter|Controller:嘿Model,你有这个搜索术语的任何匹配项吗?:“piano”[MVP|MVC]
模型:嘿,展示者|控制器,让我检查一下……[MVP|MVC]
(模型正在查询电影数据库……)[MVP|MVC]
(过了一会儿……)
-------------- 这是MVP和MVC开始分歧的地方 ---------------
模型:我为你找到了一个列表,展示者,这是它的JSON格式“[{"name":"钢琴教师","year":2001},{"name":"钢琴","year":1993}]”[MVP]
模型:有一些可用的结果,控制器。我在我的实例中创建了一个字段变量,并将其填充了结果。它的名称是“searchResultsList”[MVC]
(展示者|控制器感谢模型并回到视图)[MVP|MVC]
展示者:感谢你等待,视图。我为你找到了一系列匹配结果,并以可呈现的格式整理好了:“["钢琴教师 2001","钢琴 1993"]”。请在垂直列表中向用户展示它。同时请隐藏进度条[MVP]
控制器:感谢你等待,视图。我询问了模型关于你的搜索查询。它说已经找到了一系列匹配结果,并将它们存储在其实例内的名为“searchResultsList”的变量中。你可以从那里获取它。同时请隐藏进度条[MVC]
视图: 非常感谢你 主持人 [MVP]
视图: 谢谢你“控制器” [MVC] (现在,视图正在自问:我应该如何向用户呈现从模型获取的结果?电影的生产年份应该放在最前面还是最后面...?它应该是垂直列表还是水平列表? ...)
如果您有兴趣,我已经写了一系列关于应用程序架构模式(MVC、MVP、MVVP、清洁架构等)的文章,并附带一个Github库这里。尽管示例是为Android编写的,但基本原则可以适用于任何介质。
模型-视图-控制器
MVC 是一种软件应用程序的架构模式。它将应用程序逻辑分成三个独立的部分,促进了模块化和协作重用。它还使应用程序更加灵活,容易进行迭代。它将一个应用程序分成以下组件:
为了让这更加清晰,让我们想象一个简单的购物清单应用程序。我们只需要一个列表,列出每个需要购买的项目的名称、数量和价格。下面我们将描述如何使用MVC实现其中一些功能。
模型-视图-表示器
如果您想查看简单实现样例,请查看此处的GitHub帖子
MVC和MVP模式之间的区别是什么?
MVC模式
控制器基于行为,可以在视图之间共享
可以负责确定显示哪个视图(前端控制器模式)
MVP模式
视图与模型的耦合度更低。Presenter负责将模型绑定到视图上。
更易于单元测试,因为与视图的交互是通过接口进行的
通常视图与Presenter一一对应。复杂视图可能具有多个Presenter。
MVC = 模型-视图-控制器