为什么Qt滥用模型/视图术语?


104

我认为Qt中使用模型/视图控件的术语存在缺陷。他们其解释页上指出,他们通过合并View和Controller将MVC简化为MV,并给出了以下图片:

图片解释Qt MVC

但是我认为,他们错误地命名了对象的角色,我认为,

  1. 他们所说的带有合并Controller的View实际上只是一个View。
  2. 他们所谓的Model实际上只是Controller。
  3. 如果您真的想要一个模型,它将在他们的“数据”所在的地方。

我说的是您在应用程序中使用Qt模型/视图组件的惯常而明智的方式。原因如下:

  1. 这通常是按原样使用的Qt组件,无需添加任何特定于对象的Controller逻辑)
  2. 这几乎不是模型,只是因为您应该实现几种Qt方法,例如rowCount,columnCount,data等,这些方法与模型无关。实际上,在Controllers中可以找到典型的模型方法。当然,您可以在此处实现Controller Model逻辑,但首先这将是非常糟糕的代码设计,其次,您将合并Controller和Model而不是Controller和View所声明的状态。
  3. 如原因2所述,如果您想分离模型逻辑,那肯定不是图片上的蓝色框,而是虚线的“数据”框(当然是与真实数据通信)。

Qt的术语是错误的,还是只有我不理解?(顺便说一句,这不是学术性的问题,是因为我开始按照他们的名字对我的项目进行编码,而我很快发现,代码显然是不正确的。只有在我意识到之后,我才应该不要尝试将模型逻辑放入他们所谓的模型中)


1
MFC使用CDoc和CView为2part模型/视图GUI设置了标准-没有理由说特定的MVC是“正确的”
Martin Beckett

@Martin B:我将看一下MFC,但是,即使有不同的MVC模型,我认为它们的术语也应该保持一致,并且我认为我已经提出了有效的论据,为什么在这种情况下使用的术语不一致。他们只是简单地声明他们已经将View和Controller结合在一起,但是我认为这只是一种明显的误导。我认为没有MVC模型,所有应用程序特定的逻辑(无论是表示还是模型逻辑)都必须放在一个称为模型的对象中。
gorn 2011年

1
@Martin B:同样在qt术语下,所有模型都有通用的api,与模型结构无关,但与通用的Controller结构有关,这显然表明不正确地称其为Model。我并不是说有一个正确的MVC模型,但这并不意味着什么都可以称为MVC模型。也许它在MFC中也是有缺陷的,我可以看一下,但是我对Qt正确使用它而不是我不打算使用的MFC更感兴趣。您是否有任何很好的链接来解释MFC模型/视图分离?
gorn 2011年

1
绝对没有MVC术语的一致同意,因此您的问题可以认为是有争议的。但是,许多人会同意Martin Fowler(martinfowler.com/eaaDev/index.html)所做的出色工作。通常,控制器处理用户输入,从这个意义上讲,Qt小部件绝对可以将视图和控制器结合在一起。
阿诺德·斯彭斯

1
我了解MVC具有多种风格,但这并不意味着任何东西都可以成为MVC。Qt已经越过界限,我给出了几个原因。马丁·福勒(Martin Fowler)的确解释了不同类型的MVC,但它们都与Qt所说的MVC不够相似。最相似的是martinfowler.com/eaaDev/PresentationModel.html,但这在Presentation Model = Controller(用户交互)部分和Model(数据逻辑)之间有所区别。因此,尽管没有MVC的精确定义,但Qt并未遵循其中的任何一个。如果您可以给我链接到这样的定义,请这样做。
gorn 2011年

Answers:


78

我同意Qt的命名具有误导性。但是,我认为问题并不仅仅存在于Qt上,而是所有框架共有的问题,这些框架使我们能够在实现UI时遵循关注点分离的原则。当有人提出了这样一个框架,并找到了一种使“事物”分离的好方法时,他们总是感到必须拥有被称为“模型”的模块和被称为“视图”的模块。多年来,我一直在使用以下框架:

  • MFC
  • t
  • 摇摆
  • SWT
  • 具有MVVM的WPF

如果比较在这些框架中如何使用术语“模型”和“视图”,以及“视图”,“模型”和“控制器”(如果有)中的类的职责,您将发现有很大的差异。比较不同的概念和术语肯定会很有用,这样,从一个框架切换到另一个框架的人就有机会保持理智,但这需要大量的工作和研究。马丁·福勒(Martin Fowler)的概述很不错。

既然有这么多不同的想法什么 MVC模式可以关注一下,哪一个是正确的?我认为,发明MVC的人应该在我们想知道应该如何“正确”实施MVC的时候转向他们。在原始的Smalltalk论文中说:

该视图管理图形和/或文本输出到分配给其应用程序的位图显示部分。控制器解释用户的鼠标和键盘输入,命令模型和/或视图进行适当更改。最后,模型管理应用程序域的行为和数据,响应有关其状态信息的请求(通常是从视图),并响应更改状态的指令(通常是从控制器)。

有鉴于此,我将回答您的三个主要问题:

  1. 实际上,Qt组件“管理图形输出”和“解释鼠标和键盘输入”,因此,相对于上述定义,它的确可以称为合并视图和控制器。
  2. 我同意您将/将被迫合并Controller和Model(同样关于上述定义)。
  3. 我再次同意。该模型应仅管理应用程序域的数据。这就是他们所谓的“数据”。显然,例如,处理行和列通常与我们的应用程序域无关。

它在哪里离开我们?我认为,最好弄清楚使用“模型”和“视图”这两个术语时Qt的真正含义,并在我们使用Qt进行编程时以它们的方式使用这些术语。如果您不停地打扰它,只会减慢您的速度,而且Qt中的设置方式确实允许优雅的设计-比它们的“错误”命名约定重得多。


2
我可以说委托是Qt的控制器,因为委托接收输入并将其发送到模型,该模型通过信号更新视图。
Peregring-lk,2016年

82

简短答案

Qt的MVC仅适用于一种数据结构。在谈论MVC 应用程序时,您不应该考虑QAbstractItemModelQListView

如果您希望整个程序都采用MVC架构,则Qt并不需要这么“庞大”的模型/视图框架。但是对于程序中的每个列表/数据树,您都可以使用Qt MVC方法,该方法的确在视图中包含一个控制器。的数据是内或模型的外部; 这取决于您使用的是哪种类型的模型(自己的模型子类:可能在模型内部;例如QSqlTableModel:在模型外部(但可能在模型内部))。要将模型和视图放在一起,请使用自己的类,然后再实现业务逻辑


长答案

Qt的模型/视图方法和术语:

Qt 为其模型提供了简单的视图。它们具有内置的控制器:在大多数情况下,控制器会“选择”,选择和移动项目。也就是说,解释用户输入(鼠标单击和移动)并将适当的命令提供给模型。

Qt的模型确实是具有基础数据的模型。当然,抽象模型不会保存数据,因为Qt不知道您要如何存储它们。但是可以通过将数据容器添加到子类并使模型接口访问数据来扩展QAbstractItemModel以满足您的需求。因此,实际上,并且我认为您不喜欢这种方式,问题在于需要对模型进行编程,以便了解如何在数据结构中访问和修改数据。

用MVC术语,模型既包含数据又包含逻辑。在Qt中,由您自己决定是否将某些业务逻辑包含在模型中还是将其放置在模型中,取决于您自己。甚至不清楚逻辑的含义:选择,重命名和移动项目?=>已经实现。用它们做计算吗?=>将其放在模型子类的外部或内部。从文件存储数据或将数据加载到文件?=>将其放在模型子类中。


我个人的看法:

这是很难提供良好一般的MV(C)系统程序员。因为在大多数情况下模型是简单的(例如仅字符串列表),所以Qt还提供了现成的QStringListModel。但是,如果您的数据比字符串还复杂,则取决于您如何通过Qt模型/视图界面表示数据。例如,如果您有一个具有3个字段的结构(例如,具有姓名,年龄和性别的人),则可以将3个字段分配给3个不同的列或3个不同的角色。我不喜欢这两种方法。

我认为Qt的模型/视图框架仅在要显示简单数据结构时才有用。如果数据属于自定义类型或结构不在树或列表(例如图形)中,将变得很难处理。在大多数情况下,列表就足够了,即使在某些情况下,模型也应仅包含一个条目。特别是如果您要为一个具有不同属性的单个条目(一个类的一个实例)建模,则Qt的模型/视图框架不是将逻辑与用户界面分离的正确方法。

综上所述,我认为Qt的模型/视图框架仅在且仅当Qt的查看器小部件之一正在查看您的数据时才有用。如果您要为仅包含一个条目的模型编写自己的查看器(例如,应用程序的设置),或者您的数据不是可打印的类型,则完全没有用。


如何在(更大的)应用程序中使用Qt模型/视图?

我曾经(团队)编写过一个使用多个Qt模型来管理数据的应用程序。我们决定DataRole为每个不同的模型子类创建一个保存不同类型的实际数据的实际数据。我们创建了一个外部模型类,称为Model容纳所有不同的Qt模型。我们还创建了一个外部视图类,称为View保存与之内的模型连接的窗口(窗口小部件)Model。因此,此方法是扩展的Qt MVC,适合我们自己的需求。无论ModelView类本身没有什么关系了Qt MVC。

我们把逻辑放在哪里?我们创建了一些类,它们通过从源模型中读取数据(当它们更改时)并将结果写入目标模型中来对数据进行实际计算。从Qt的角度来看,此逻辑类将是视图,因为它们“连接”到模型(不是用户的“视图”,而是应用程序的业务逻辑部分的“视图”)。

控制器在哪里?在原始MVC术语中,控制器解释用户输入(鼠标和键盘),并向模型发出命令以执行请求的操作。由于Qt视图已经解释了诸如重命名和移动项目之类的用户输入,因此不需要这样做。但是我们需要的是超越Qt视图的用户交互解释。


最令人烦恼的是,您必须根据所需的视图实现完全不同的Qt Model类。列表视图的模型将无法正确支持树形视图,反之亦然。规范的MVC模型可以支持多种不同的视图类型。
smerlin

3
@smerlin:我认为那是不正确的。QListView和QTreeView都只需要QAbstractItemView接口,这意味着该接口的自定义子类或像QStandardItemModel这样的具体类都应满足两者的要求。您可以驾驶一棵树并列出一个模型。
jdi 2012年

1
@jdi:在某些情况下,您的数据既是列表又是树……例如,您想将文件系统显示为树,或将所有文件显示为列表。Qts模型不允许这样做。QAbstractItemModel的支持树视图的实现仅允许将根目录中的所有文件/目录显示为列表,而不能将所有文件显示为列表。而且不要说将树数据显示为列表是没有用的。例如,如果您将文件显示为列表,则可以轻松地对它们进行排序,例如找到文件大小最大的文件,而树状视图则不允许这样做。
smerlin

1
就是说,代理模型更像是您的视图(因为它修改了如何查看数据),因此应该属于您的视图。如果您读了我很长的答案:在“大” View类中,您应该添加代理模型,该代理模型将树模型作为其基础模型,并由文件系统列表视图使用。正如您所说的:同一数据不应有两个模型。决不!(但是代理模型不算作单独模型。)
leemes 2012年

1
@SamPinkus这是因为此问题没有明确的。另外,还有的不同实现QAbstractItemModel,其中一些是MVC的模型,而有些则不是。
leemes 2014年

12

术语不是对是错,有用或无用。

您可能会稍微改变问题,然后问为什么Qt对MVC更不友好。答案是,早期的Qt开发人员认为,GUI应用程序中的V与C之间的去耦会导致坏的Vs和Cs。QWidget的设计试图简化将鼠标输入操作与像素输出决策紧密绑定的过程,您会发现这并不是通往MVC的道路。


我明白了你的意思,基本上我会问为什么Qt对MVC不太友好,但是当Qt文档中使用的MVC术语与通常使用的MVC有所不同时,这很难做到。当使用广泛的术语并且某人使用的术语与世界其他地方有很大不同时,我倾向于认为它不仅无用,而且会造成明显的错误和混乱(混淆使我首先问了这个问题。地点)。如果您有链接到正在讨论或解释的这些事情,我将非常感兴趣。谢谢
gorn

我不能说为什么Qt文档现在以他们的方式谈论MVC。我很久以前就离开了Trolltech,自从离开以来对文档所做的一些事情感到困惑。(不过,在我的博客上,我有时会对此
大声疾呼

您是否对Qt中的MVC术语达成共识?是在编写代码时使用,还是仅在文档编写过程中使用。
gorn

在Trolltech工作期间,我们没有在Qt文档中使用“ MVC”一词。总的来说,我认为最好记录其中存在的内容,而不是写下不存在的内容。但是,在令人不快的地方,您可以找出是谁添加了该文本,然后直接添加了该人。
arnt 2012年

1
不同的评论。我们在Qt的设计和早期实现阶段(当时Trollech是一家三人公司)曾讨论过MVC,并且我们评估了“正确”使用MVC的GUI工具箱,我不记得它的名字了。我们认为该工具包使用起来很糟糕,而MVC就是其中的主要原因。
arnt 2012年

3

为模型的功能是对信息请求作出回应,我觉得没有什么不对的定义等方法rowCountcolumnCount等等。我觉得模式是某种包装的数据源(不管它是什么SQL表或只是一个数组) ,它以标准格式提供数据,因此您应根据数据源结构定义方法。


2

我相信他们的术语是正确的...尽管在实际应用中,我发现根据您的抽象级别模糊模型,视图和控制器之间的界限非常容易:一个级别的视图可能是更高级别的模型。

我感到困惑来自他们的QAbstractModelItem类。此类不是模型项,而是它是模型的接口。为了使他们的视图类与模型建立接口,他们必须为模型创建通用的抽象接口。但是,模型可以是单个项目,项目列表,项目的2个或更多维的表格等。因此它们的界面必须支持所有这些模型变体。诚然,这使模型项变得相当复杂,并且使它与实际模型一起使用的粘合代码似乎确实使隐喻有些延伸。


尽管我同意QAbstractModelItem类,但我也认为即使没有这种复杂性,它们的MVC也会被错误命名。您能否解释一下为什么您认为他们的术语是正确的。我很想听听为什么我的三个论点都不正确。
2011年

0

我认为...他们所谓的Model实际上只是Controller。

不,他们的“模型”绝对不是控制器。

控制器是用户可见控件的一部分,这些控件可以修改模型(并因此间接修改视图)。例如,“删除”按钮是控制器的一部分。

我认为经常会感到困惑,因为许多人看到诸如“控制器修改模型”之类的东西,并认为这意味着其模型上的变异函数,如“ deleteRow()”方法。但是在经典MVC中,控制器特别是用户界面部分。改变模型的方法只是模型的一部分。

自从MVC发明以来,其在控制器和视图之间的区别就变得越来越紧张。想想一个文本框:它既可以显示一些文本,又可以让您对其进行编辑,因此它是视图还是控制器?答案必须是它是两者的一部分。早在1960年代,当您使用电传打字机时,区别就更加清晰了-想想ed-但这并不意味着那时的用户情况会更好!

的确,他们的QAbstractItemModel比模型通常具有更高的级别。例如,其中的项目可以具有背景色(从技术上讲是画笔),这绝对是一种具有视觉效果的属性!因此,有一个论点是QAbstractItemModel更像视图,而您的数据就是模型。事实是它介于视图和模型的经典含义之间。但是我看不到它是一个控制器。如果有的话就是使用它的QT小部件。

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.