MVC中的“ C”真的必要吗?


37

我了解模型和视图在“模型-视图-控制器”模式中的作用,但是我很难理解为什么需要控制器。

假设我们正在使用MVC方法创建国际象棋程序;游戏状态应该是模型,GUI应该是视图。在这种情况下,控制器究竟是什么?

它是否只是一个单独的类,具有在您单击图块时将要调用的所有功能?为什么不只在视图本身中对模型执行所有逻辑?


1
就个人而言,这就是我要做的在某些情况下,MVC别无选择,但我无法忍受。
Mike Dunlavey 2012年

10
三个词...“关注分离”。
特拉维斯J

4
.net之前的几乎所有Windows程序都使用没有控制器的Doc-View。这似乎是相对成功的。
马丁·贝克特

马丁,不可改变的独角兽。
独立

我在下面回答了,但是我要补充一点,您可以构建不带不同控制器类的应用程序,但这不是MVC。您假设“ MVC方法”,所以是的,控制器起着重要的作用。如果您选择的不是MVC,则很可能没有任何控制器。
Caleb 2013年

Answers:


4

以您的示例为例,主计长将决定什么是合法的举动。控制器将使用从模型接收到的信息,让视图知道在启动时如何在板上放置零件。控制器可以处理更多事情,但关键是考虑该层上的业务逻辑。

有时候,Controller要做的就是来回传递信息,例如注册页面。有时,Controller是开发中的困难部分,因为在该层需要执行很多事情,例如执行规则或执行复杂的数学运算。不要忘记控制器!


36
“以您的例子为例,主计长将决定是否采取法律行动”。这是糟糕的例子:(这样的逻辑也要在示范否则,你的逻辑控制器和模型之间的分裂。
角钱

6
@Dime-模型不应该包含任何逻辑。控制器将是一个保持逻辑,因此是“控制”。
特拉维斯J

34
@TravisJ不太同意。控制并不意味着知道如何工作。这是关于控制这些对象的作用。因此,执行工作的逻辑将在模型中,而控制器将控制使用哪个模型来执行动作的必要要求等。我认为控制器中的太多逻辑将成为控制器污点的秘诀……
dreza

25
OOP的全部重点是使数据和行为的内聚位保持在一起并在内部进行封装。“模型”对行为和数据进行建模。
MISKO

12
-1控制器不得包含业务逻辑。该模型 “应用程序”,它保存数据并为应用程序中可能发生的一切提供可调用的例程。不一定全部在一个文件或类中。该视图可视化模型的状态。控制器将模型/视图与现实世界/输入联系起来。想要将该应用 “发布” 为网络应用吗?只需要一个处理HTTP和适当的基于HTML的视图的控制器。想要使用命令行界面访问您的应用程序吗?只需要一个合适的控制器和视图。在这些情况下,模型(业务逻辑)永远不会改变。
deceze

39

为什么不只在视图本身中对模型执行所有逻辑?

控制器是将模型和视图绑定在一起的胶水,也是使它们分开的绝缘材料。该模型应该对视图一无所知,反之亦然(至少在Apple的MVC版本中)。控制器的作用类似于双向适配器,将视图中的用户操作转换为消息并传递给模型,并使用模型中的数据配置视图。

使用控制器来分离模型和视图,可以使您的代码更具可重用性,可测试性和灵活性。考虑一下您的国际象棋示例。该模型当然将包括游戏状态,但是它可能还包含影响游戏状态更改的逻辑,例如确定移动是否合法以及确定游戏结束的时间。该视图显示棋盘和棋子,并在棋子移动时发送消息,但它对棋子背后的含义,每枚棋子如何移动等一无所知。控制器既了解模型,又了解视图程序的整体流程。当用户点击“新游戏”按钮时,它是一个控制器,它告诉模型创建游戏并使用新游戏的状态来设置棋盘。如果用户采取行动,

通过将模型和视图分开来查看您得到了什么:

  • 您可以更改模型或视图,而无需更改其他视图。更改其中任何一个时,您可能都必须更新控制器,但这在某种程度上是优点的一部分:程序中最有可能更改的部分集中在控制器中。

  • 模型和视图均可重用。例如,您可以使用带有RSS提要的相同棋盘视图作为模型来说明著名游戏。或者,您可以使用相同的模型,并用基于Web的界面替换视图。

  • 为模型和视图编写测试很容易,以确保它们按应有的方式工作。

  • 模型和视图通常都可以利用标准部分的优势:模型的数组,映射,集合,字符串和其他数据容器;按钮,控件,文本字段,图像视图,表格和其他视图。


1
在用于桌面应用程序的原始MVC架构中,视图是活动类,可直接观察模型,并与控制器断开连接。
凯文·克莱恩

所有答案的问题在于,关于MVC的解释与发布的人一样多。并且上面列出的好处仅适用于MVC的一种特定解释。如果将大多数逻辑放入控制器(或模型)中,并在控制器上进行View调用/启动特定方法调用,则这将使Controller / Model组合成为独立的并且非常可重用。在大多数情况下,这是需要的新视图。我从不需要重用视图。即使是您的RSS示例,也可以使用新视图轻松处理,该视图使用您的旧视图和中间的RSS层。
Dunk

2
@Dunk:至关重要的是将业务逻辑与用户界面分开,以便可以直接测试业务逻辑。
凯文·克莱恩

@Kevin:我完全同意,业务逻辑不属于用户界面。但是,控制器不需要成为用户界面的一部分。我的意思是说有很多定义。按照一个人的定义,控制器处理按钮的按下,而另一个人则将其作为视图的一部分。如果视图知道如何将操作员的动作(即按钮按下/元素选择)转换为应用程序请求,则Controller / Model几乎可以与几乎任何类型的用户界面(包括GUI,Console或网络界面)重用。
Dunk

1
@Dunk:我想您可以调用任何您喜欢的“控制器”,但是在MVC架构中,控制器依赖于用户界面,因此是用户界面的一部分。
凯文·克莱恩

7

有许多不同的方法来实现这种通用设计模式,但是基本思想是根据需要分离各种问题。在某种意义上,MVC是一个很好的抽象:

Model:表示该数据,可能意味着
视图View:表示用户界面,这可能意味着
控制器Controller:表示导致该模型和视图进行交互的粘合剂,无论可能意味着什么

它非常灵活,因为它没有指定很多内容。很多人浪费大量带宽争论每个元素可能意味着什么,应该使用什么名称代替这些细节以及是否确实应该包含3个或2个或4个或5个组件的细节,但这却没有一定程度。

这个想法是要分离出不同的逻辑“块”,以便它们不会重叠。将演示文稿放在一起,将数据放在一起,将逻辑放在一起,将通信放在一起。依此类推。在某种程度上,这些关注领域重叠的越少,就越容易用它们做有趣的事情。

这就是您真正要担心的。


3
胶水,胶水,我喜欢这个定义,它是如此正确:整个模型应被命名为MVG,人们将不再挠头寻找无处可寻的优雅。
ZJR 2012年

1
+1表示“胶水”;这也意味着它是最适合用脚本语言完成的部分(因为那些语言往往擅长粘贴)。
Donal Fellows 2013年

@DonalFellows我很喜欢那个想法。将2个完全不同的实体“粘合在一起”的东西需要很大的灵活性,弱类型脚本语言(即JavaScript)促进了这种灵活性
Zack Macomber

4

到目前为止所有好的答案。我的两分钱是,我喜欢认为控制器主要是由诸如“什么和在哪里?

  • 我被问到是否可以将一个棋子(视图)移动到x。难道是
    允许的?我不确定,但我知道在哪里问谁(模型)。
  • 有人要求我保存数据。我该怎么办?我知道在哪里问!我们不知道如何保存数据或将数据保存到何处,但是该Repository类应该知道。我将其转发并处理。
  • 我必须向用户显示模型将其移动到的当前棋子位置。不知道我要显示绿色还是黄色?Bah在乎,我知道有一个可以处理此问题的视图,因此我将传递数据,他们可以决定如何显示数据。

这些小片段是我试图记住MVC试图传达的抽象和概念的示例。我的三个主要思考过程是什么,在哪里和如何进行。

什么和在哪里=>控制器如何以及何时=>模型和视图

从本质上讲,我的控制器动作往往很小而紧凑,阅读时往往看起来像是在浪费时间。在仔细检查中,他们充当交通信号员,将各种请求传递给适当的工人,但自己并未进行任何实际工作。


2

控制器可以帮助抽象化视图和模型的接口,这样它们就不必直接相互了解。一个对象必须知道的越少,它就变得越容易携带并且可以进行单元测试。

例如,模型可以通过一个控制器来播放自身的另一个实例。或者联网的控制器可以将两个玩家的View对象连接在一起。或者这可能是一个图灵测试,没人知道。


2

当您处理事件处理程序时,它确实发挥了作用,但是您仍然需要控制器来处理视图和模型之间的交互。理想情况下,您不希望视图对模型有所了解。考虑一下,您是否希望jsp直接进行所有数据库调用?(除非它类似于登录查找。)您希望视图呈现数据并且不包含任何业务逻辑,除非它是视图呈现逻辑,但不是业务逻辑本身。

在GWT中,您可以使用MVP进行更清晰的分离。视图中没有任何业务逻辑(如果操作正确)。演示者充当控制器,并且视图不了解模型。模型数据只需传递到视图即可。


1

文档视图(即模型视图)是大多数用MFC编写的Windows应用程序的标准模型,因此它必须在很多情况下都可以使用。


1

我了解模型和视图在“模型-视图-控制器”模式中的作用,但是我很难理解为什么需要控制器。

您确定吗?(至少如最初所述)该模型的重点是领域模型。该视图应该向用户显示域模型。控制器应该将低电平输入映射到高级模型语音。据我所知,推理是基于以下方面:A)SRP的高水平使用。B)模型被认为是应用程序的重要组成部分,因此请不要使用那些不重要且变化更快的东西。C)易于测试(和可编写脚本)的业务逻辑。

试想一下,如果您想使您的Chess程序可供盲人使用,请将视图换成可听见的版本,并使用与键盘配合使用的控制器。假设您要通过邮件添加游戏,请添加一个接受文本的控制器。网络版游戏?从套接字接受命令的控制器将完成此工作。给它添加一个漂亮的3d渲染,一个很棒的新视图。必要的零模型更改国际象棋仍然是国际象棋。

如果将输入与模型表示混合在一起,则会失去该功能。突然国际象棋不是国际象棋,它是带有鼠标的国际象棋,不同于带有键盘或网络连接的国际象棋。


0

我认为MVC是愚蠢的,也许在特定领域它能很好地工作,但就我个人而言,即使我写的网站也不适合MVC。有一个原因为什么您听到前端,后端却从不听到数据库端或其他端

IMO应该有一个API(后端)和使用该API的应用程序(前端)。我猜您可以将GET请求称为控制器(仅调用后端api),并将html称为视图,但是我通常听不到人们将视图视为纯html或将模型作为后端API的说法。

IMO一切都应该是可靠的API。实际上,它们不需要坚固(如干净整洁的结构),但其内部结构应保持私有状态,并且api的应用程序/前端/外部不应说数据库连接,也不能进行原始查询。

现在,如果您的代码/设计涉及粘合就可以了。如果在您的国际象棋游戏中,您可以编辑一些标记以使GUI外观化,则gui会收集坐标/输入并调用MovePiece(srcPosition,dstPostion)(这可能会返回bool或枚举,以表明是否有效) ),并且您可以在模型中包含所有逻辑,然后确定将其命名为MVC。但是,我仍然按照类和API来组织事物,并确保没有任何涉及所有事物的厨房接收器类(也没有任何API必须了解所有事物)。


欢迎您提出您的意见,但是此答案并非试图解决OP的问题。
卡雷布(Caleb),

0

考虑一个显示静态网页的浏览器。该模型是HTML。视图是屏幕上的实际结果。

现在添加一些JavaScript。那就是控制器。当用户单击按钮或拖动某些东西时,事件将发送到JavaScript,它决定要做什么并更改基础HTML(模型),浏览器/渲染器将这些更改显示在屏幕上(视图)。

也许单击了另一个按钮,该事件被发送到某个处理程序(控制器),并且可能导致请求将其他数据发送到Web服务。然后将结果添加到HTML(模型)。

控制器响应事件并控制模型中的内容,从而控制屏幕/视图中的内容。

退一步,您可以将整个浏览器视为View,将服务器视为Controller,将数据视为Model。当用户在浏览器中单击事件按钮发送到服务器(控制器)时,它将事件收集为HTML页面(模型),并将其发送回浏览器以显示(查看)

在服务器中,无论是asp,php还是java,“代码”(控制器)都将接收click事件并查询数据库或文档存储库(模型)并创建HTML。从服务器的角度来看,其所有操作的结果是其基础数据存储区(模型)的视图(HTML)。但是从客户端的角度来看,其对服务器的请求结果是其模型(HTML)

即使您混淆HTML中的JavaScript或HTML中的PHP,模型,视图,控制器仍然存在。即使您认为对服务器的请求和从服务器返回的响应只是一条简单的双向路,仍然存在一个模型,一个视图和一个控制器。


-2

以我的经验,在传统的桌面mvc gui程序中,控制器最终变成了意大利面。大多数人不花时间来淘汰控制器类。

四人帮的书说:

Smalltalk MVC中的设计模式

类[KP88]的模型/视图/控制器(MVC)三元组用于在Smalltalk-80中构建用户界面。查看MVC内部的设计模式应该可以帮助您理解“模式”一词的含义。

MVC由三种对象组成。模型是应用程序对象,视图是其屏幕显示,控制器定义了用户界面对用户输入的反应方式。在MVC之前,用户界面设计倾向于将这些对象放在一起。MVC使它们解耦以增加灵活性和重用性。

MVC通过在视图和模型之间建立订阅/通知协议来分离它们。视图必须确保其外观反映模型的状态。每当模型的数据更改时,模型都会通知依赖于它的视图。作为响应,每个视图都有机会进行自我更新。这种方法使您可以将多个视图附加到模型以提供不同的演示文稿。您也可以为模型创建新视图,而无需重写模型。

下图显示了一个模型和三个视图。(为简单起见,我们省略了控制器。)该模型包含一些数据值,并且定义电子表格,直方图和饼图的视图以各种方式显示这些数据。模型的值更改时,模型将与其视图进行通信,并且视图与模型进行通信以访问这些值。

从表面上看,此示例反映了将视图与模型分离的设计。但是该设计适用于一个更普遍的问题:将对象去耦,以便对一个对象的更改可以影响其他数量的对象,而无需更改的对象知道其他对象的详细信息。观察者(第293页)设计模式描述了这种更一般的设计。

MVC的另一个功能是可以嵌套视图。例如,按钮的控制面板可以实现为包含嵌套按钮视图的复杂视图。对象检查器的用户界面可以包含可在调试器中重用的嵌套视图。MVC通过CompositeView类(View的子类)支持嵌套视图。CompositeView对象的行为就像View对象一样;可以在可以使用视图的任何地方使用复合视图,但是它也包含和管理嵌套视图。

同样,我们可以将其视为一种设计,使我们可以像对待其中一个组件一样对待复合视图。但是该设计适用于更普遍的问题,该问题在我们想要对对象进行分组并将组像单个对象一样对待时发生。复合(163)设计模式描述了这种更为通用的设计。它使您可以创建一个类层次结构,其中一些子类定义基本对象(例如Button),而其他类定义组合对象(CompositeView),这些对象将基本元素组装成更复杂的对象。

MVC还使您可以更改视图对用户输入的响应方式,而无需更改其视觉表示。例如,您可能想更改它对键盘的响应方式,或者让它使用弹出菜单而不是命令键。MVC将响应机制封装在Controller对象中。控制器具有一类层次结构,可轻松创建新控制器作为现有控制器的变体。

视图使用Controller子类的实例来实现特定的响应策略。要实施不同的策略,只需将实例替换为其他类型的控制器即可。甚至有可能在运行时更改视图的控制器,以使视图更改其对用户输入的响应方式。例如,可以禁用视图,以便仅通过为其提供一个忽略输入事件的控制器即可不接受输入。

视图-控制器关系是策略(315)设计模式的一个示例。策略是表示算法的对象。当您要静态或动态替换算法,算法有很多变体或算法具有要封装的复杂数据结构时,此功能很有用。

MVC使用其他设计模式,例如工厂方法(107)为视图指定默认控制器类,装饰器(175)向视图添加滚动。但是,MVC中的主要关系由观察者,合成和策略设计模式给出。


1
看起来整个帖子减去前两段都是从Design Patterns逐字记录的。我已将该部分格式化为引号,以便读者理解-如果我引用的段落是您自己的,请编辑。
Caleb 2013年

1
我必须不同意您的观点,即“控制器最终将意大利面混入视图中”。可能会因所使用的平台和框架而异,但是在Cocoa和Cocoa Touch编程中,创建合适的控制器要比忽略它们更为普遍。如果Objective-C程序员忽略了其中一种类别,那么几乎可以肯定会遭受该模型的破坏。
Caleb 2013年

如果您同意这是MVC的“正确”解释,那么MVC绝对不会买任何东西。也可能只是MV,而忽略了C,因为每次创建新View时,还需要创建一个新Controller。因此,除了理论上分离关注点的原因之外,尽力将它们分开是什么意思。
Dunk 2013年
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.