什么是Model-View-Controller(MVC)相比于Model-View的优势?

6
有没有人能举例说明为什么使用MVC而不是仅用一个简单的模型和视图会更有优势?
注意:无论它被称为MVC还是MVP(Model-View-Presenter),我说的是指视图接收输入,然后控制器将通过解释输入以执行由模型完成的某些操作来响应输入事件。当模型发生变化时,视图将通过响应模型的事件来更新自己。
如果只是让模型对视图进行事件响应,反之亦然,这有何不利之处?
在MVC中,如果我以影响控制器的方式更改了模型,那么我就必须对控制器进行更改;在“模型-视图”中,如果我更改了模型,则必须更新视图。
因此,似乎添加“控制器”部分会引入复杂性?
3个回答

4
在MVC中,模型对其环境是盲目的,视图也可以是这样的——将其事件(盲目地)传递给控制器,后者更了解视图和模型。因此,归根结底,控制器是系统中“不可重用”的一部分,因为它是最具上下文感知能力的组件。
如果我以某种方式更改了影响控制器的模型...
那么模型应该以一种简单的CRUD方法公开,以使使用这些方法的人不必知道传递的更新对象的任何信息,也不必知道模型内部实际发生了什么。
这意味着视图需要做一些工作,通过创建传递的记录,因为控制器应该是无状态的,而视图更加持久化。控制器会触发并“启动”,使用传递的对象进行工作,并且没有状态。
传递的数据是通过某种通用约定创建的。
让我再进一步。假设您有一个视图、一个表格和一个控件,其中控件的启用属性取决于在网格中选择的项目——您可以创建一个处理这两个控件和这个逻辑的视图,这可能是这样一个简化示例的方法。
但是,您的视图越原子化,它们就越可重用,因此您为每个控件创建一个视图。现在,您正在查看一种情况,其中视图必须知道彼此才能为正确的通知注册自己...
这就是控制器发挥作用的地方,因为我们希望将所有这些依赖关系都放在长期可丢弃的控制器上。因此,控制器管理此类型的视图到视图的通知方案。
现在,您的视图可以无知,并且是独立的,因此可重用。
您可以编写视图而无需了解系统或所谓的“业务逻辑”。您可以编写模型而无需过多了解您的目标(尽管调整模型以使其能够返回您所考虑的数据集有所帮助)...但是,控制器是最后一个,您必须先确定前两个,然后才能将它们连接起来。
这里还有一件事要考虑——正如模型应该抽象出并提供对其管理的数据底层实现的通用接口一样(客户端不知道数据来自数据库、文件、程序设置等),视图也应该抽象出其使用的控件。
因此,最终这意味着视图不应该(下面有一个警告)具有以下函数/属性:
public property BackgroundColor{get;set}

Nor

public function ScrollBy(x,y){}

但实际上:
public SetProp(string name, object val){}

并且
public DoCmd(string name, object val){}

这有点牵强,但请记住我说过最终目标是……你会问这为什么是一个好主意?
考虑到可重用性,想象一下,也许有一天你想从WinForms移植东西到Flex,或者只是想使用一个新颖的控件库,它可能不会暴露相同的功能。
我在这里说“移植”,但这真的不是目标,我们不关心移植这个特定的应用程序,而是使底层MVC元素足够通用,以便可以跨越到一个新的风格——内部保持一致和能力独立的外部接口不变。
如果你不这样做,那么当你的新风格出现时,所有对视图属性的硬引用(可能是可重用/可重构/可扩展的)在控制器中都必须被修改。
这并不意味着这样的通用设置器和命令必须成为所有视图能力的接口,而是它们应该处理“边缘情况”属性以及您可以以传统的硬链接方式公开的常规属性/命令。把它看作是一个“扩展属性”处理程序。
这样,(再次牵强),假设你正在构建一个框架,其中你的按钮不再具有buttonIcon属性。这很酷,因为你有预见性地创建了一个按钮视图接口,其中buttonIcon是一个扩展属性,在视图内部,当它接收到set/get时,你的条件代码现在会执行空操作。
总之,我想说的是MVC的编码目标应该是为其底层组件提供通用接口,使得在编写控制器时,你不必过多考虑你正在控制谁。虽然控制器似乎被设置成长期可重用性的牺牲品,但这并不意味着所有的控制器都注定要消亡。
它们希望是小的,因为他们的大量“思考”已经转移到了半智能模型和视图以及其他控制器(例如:控制器对网格进行排序或操作TreeView)——所以它们很小,可以很容易地查看和确定在你的下一个项目中是否可以重用,或者克隆和调整以变得合适。

我会举一个简单的例子,说明为什么我没有看到优势。假设我们谈论的是一辆汽车。当 Car View 的 Accelerate 按钮被点击时,Controller 会响应调用 Model.Accelerate(),然后 Model 的速度会改变,View 会响应 Model 的事件,并更新屏幕上的速度读数。现在,在 Model-View(MV)中,当按钮被点击时,View 调用 Model.Accelerate(),同样的事情发生了,而不涉及控制器。如果在 MVC 中,我将 Accelerate() 更改为 IncreaseSpeed(),我必须更新控制器以调用此方法。对于 MV,我将进行相同的更新,但是针对的是 View。 - Saleh Al-Abbas
继续上一个评论:因此,这些优点仅适用于以下情况:1. 您的观点不特定于您当前的项目(这很少见)。2. 我想不出任何其他情况!!!(也许是缺乏经验)。 - Saleh Al-Abbas
你有一个一对一的例子。视图必须完全了解模型的所有影响方式。这是一个依赖关系的聚集地。而控制器则将此聚集分开 - 每个事件都有一个单独的控制器 - 如果需要在事件发生时对模型进行更高级的修改,则在控制器中进行修改(即,控制器仅适用于一个事件,因此可以增加其重复使用的可能性)。按照您的方式,每次增强/添加都会降低视图的可重用性。 - Mark Robbins
视图唯一需要知道的是它正在监听的消息,甚至当视图被注册时,这也可以由外部系统处理。因此,视图只是一个“MVC控件”,除了几个方面——它知道如何解析给定的数据(或者有设置来显示它想要的内容,或者即使这种解析很复杂,也可以将其卸载到中介器上),并且它知道如何操作和挂钩底层控件的事件。它是使用MVC所需的“联系”,并且在项目中具有自己的开发路径,不断改进。 - Mark Robbins
1
关于视图的另一个要点是,它们不必与特定控件绑定,而可以管理全局问题。例如,您可以拥有一个焦点中介者(也是一个视图),用于跟踪和处理焦点相关问题(禁用其他控件等)。或者一个负责在所有控件上管理全局外观方案的视图。我将其视为功能层。 - Mark Robbins
显示剩余2条评论

1

通过将工作流逻辑与领域逻辑分离,它实际上减少了复杂性。这也使编写单元测试更容易,并使您的应用程序更易于维护和扩展。

想象一下,如果您想添加一个新的数据类型。使用上述方法,您可能会在新类中复制许多工作流逻辑,因为它很可能与领域逻辑紧密耦合。

将工作流逻辑分离到控制器中所涉及的纪律性使得您之间的工作流和领域逻辑之间的依赖关系更少。然后,添加新数据类型将变得更加简单,您可以创建新的领域对象并查看可以重用多少控制器,例如从控制器超类继承。

这也将使将来更容易更改框架-模型可能不会太多更改,因此更具可移植性。

话虽如此,根据您用作演示层的内容,您可能需要研究MVVM: MVVM相对于MVC的优势


1

MVC/P(我这里指的是Supervising Controller)相对于MV的优势包括:

  • 如果需要,您可以在控制器中处理复杂的数据绑定代码。

  • 您可以在没有UI测试框架的情况下测试复杂的演示逻辑。

  • 您还可以让平面设计师制作您的视图,而不看到您的代码,并且在修复视图时不会弄乱您的代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接