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

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

图片说明Qt MVC

不过我觉得,他们误称了对象的angular色,我认为,

  1. 他们称之为“合并控制器视图”实际上只是一个视图。
  2. 他们所称的模型实际上只是控制器。
  3. 如果你真的想要一个模型,那么它将会是“数据”的地方。

我正在谈论通常和理智的方式,你会在你的应用程序中使用Qt模型/视图组件。 原因如下:

  1. 这是典型的Qt组件,按原样使用,而不添加任何特定于您的对象的控制器逻辑)
  2. 这几乎不是模型,只是因为你应该实现像rowCount,columnCount,数据等几个Qt方法,这与你的模型没有任何关系。 实际上在控制器中有典型的模型方法。 当然,你可以在这里实现控制器模型逻辑,但首先它将是非常糟糕的代码devise,其次,你会合并控制器和模型而不是控制器和视图,因为他们的状态。
  3. 如理由2所述。如果你想分离模型逻辑,它肯定不是图片上的蓝色框,而是虚线的“数据”框(当然与真实的数据通信)。

Qt在术语上是错误的,还是只有我不明白? (顺便说一句,之所以不是学术问题,是因为我已经开始在命名之后编写我的项目了,我很快就发现,代码显然是不正确的不要试图把模型逻辑放在他们所称的模型中)

我同意你的观点,Qt的命名是误导性的。 然而在我看来,这个问题不仅仅是Qt的问题,而是由所有的框架共享的,这些框架使我们能够在实现UI的时候遵循分离原则。 当有人提出这样一个框架,并find一种保持“事物”分离的好方法时,他们总是觉得有模块称为“模型”,而其他人称之为“视图”。 多年来,我一直与这些框架合作:

  • MFC
  • Qt的
  • 摇摆
  • SWT
  • 带有MVVM的WPF

如果比较这些框架中“模型”和“视图”这两个术语的用法,以及“视图”,“模型”和“控制器”(如果有的话)中的类有什么责任,发现有非常大的差异。 对不同的概念和术语进行比较肯定是有用的,因此从一个框架转换到另一个框架的人有机会保持理智,但这需要大量的工作和研究。 Martin Fowler的概述是一个很好的解读。

既然有这么多不同的想法MVC模式可以是什么样子,哪一个是正确的? 在我看来,当我们想知道如何“正确地”实现MVC的时候,那些发明MVC的人应该被转向。 在原始的小窍门文件中说:

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

鉴于此,我会回答你的三个主要问题:

  1. 事实上,一个Qt组件“pipe理graphics输出”和“解释鼠标和键盘input”,所以就上面的定义而言,它确实可以被称为合并视图和控制器。
  2. 我同意你/将被迫合并控制器和模型(再次就上面的定义)。
  3. 我再次同意。 该模型只应该pipe理应用程序域的数据。 这就是他们所说的“数据”。 显然,处理行和列通常与我们的应用程序域无关。

它在哪里离开我们? 在我看来,最好是在使用“模型”和“视图”这两个术语时,找出Qt的真正含义,并且在用Qt进行编程的时候以他们的方式使用这些术语。 如果你一直在困扰它只会让你放慢速度,而Qt中的事情确实让devise更加优雅 – 这更多的是他们“错误”的命名规则。

简短的回答

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

如果你想为你的整个程序提供一个MVC架构,Qt就没有这么庞大的模型/视图框架。 但是,对于程序中的每个数据列表/树,您可以使用Qt MVC方法,在其视图中的确有一个控制器数据在模型之内或之外; 这取决于你正在使用什么types的模型(自己的模型子类:可能在模型中;例如QSqlTableModel:外部(但可能caching在模型中))。 把你的模型和视图放在一起,使用自己的类,然后实现业务逻辑


长答案

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

Qt为他们的模型提供了简单的视图 。 他们有一个内置的控制器 :select,编辑和移动项目是大多数情况下控制器“控制”的东西。 也就是说,解释用户input(鼠标点击和移动),并给模型提供适当的命令。

Qt的模型确实是具有潜在数据的模型 。 抽象模型当然不包含数据,因为Qt不知道如何存储它们。 但是, 可以通过将数据容器添加到子类并使模型接口访问数据来扩展QAbstractItemModel以满足您的需求。 所以实际上,我假设你不喜欢这个,问题是需要编程模型,所以在数据结构中如何访问和修改数据。

在MVC术语中,模型包含数据逻辑 。 在Qt中,是否将模型中的一些业务逻辑包含在模型中,或者将其放在外面,是一个“视图”。 甚至不清楚逻辑是什么意思:select,重命名和移动项目? =>已经实现。 与他们做计算? =>将它放在模型子类的外部或内部。 从/向文件存储或加载数据? =>把它放在模型子类中。


我的个人意见:

为程序员提供一个好的通用的MV(C)系统是非常困难的。 因为在大多数情况下,模型是简单的(例如,只有string列表)Qt还提供了一个准备使用的QStringListModel。 但是如果你的数据比string更复杂,那么你想怎样通过Qt模型/视图界面来表示数据呢。 例如,如果你有一个带有3个字段的结构(假设有姓名,年龄和性别的人),你可以把3个字段分配给3个不同的列或者3个不同的angular色。 我不喜欢这两种方法。

我认为Qt的模型/视图框架只在需要显示简单的数据结构时才有用。 如果数据是自定义types的数据,或者不是以树或列表forms(例如graphics)构造,则处理变得困难。 在大多数情况下,列表是足够的,甚至在某些情况下,一个模型应该只有一个单一的条目。 特别是如果你想模拟一个具有不同属性的单个条目(一个类的一个实例),Qt的模型/视图框架不是从用户界面分离逻辑的正确方法。

总结一下,我认为Qt的模型/视图框架是有用的,当且仅当您的数据被Qt的查看器小部件之一查看 。 如果您要为只有一个条目的模型(例如应用程序的设置)或者您的数据不是可打印types编写自己的查看器,则完全没有用处。


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

我曾经写过(在一个团队中)使用多个Qt模型来pipe理数据的应用程序。 我们决定创build一个DataRole来保存每个不同模型子类的不同自定义types的实际数据。 我们创build了一个名为Model的外部模型类,它包含所有不同的Qt模型。 我们还创build了一个名为View持有窗口的外部视图类(小部件),这些ViewModel的模型相连。 所以这个方法是一个扩展的Qt MVC,适应了我们自己的需求。 ModelView类本身与Qt MVC没有任何关系。

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

控制器在哪里? 在最初的MVC术语中,控制器解释用户input(鼠标和键盘),并向模型发出命令以执行请求的操作。 由于Qt视图已经像重命名和移动项目一样解释用户input,所以这不是必需的。 但是我们需要的是对超越Qt视图的用户交互的解释。

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

您可能会稍微改变一下问题,并问为什么Qt不是更适合MVC。 答案是,早期的Qt开发人员认为,在GUI应用程序中将V从C中解耦出来,会导致Vs和Cs都不好。 QWidget的devise试图简化鼠标input操作与像素输出决策的紧密结合,您可以看到这不是通向MVC的道路。

由于Model函数是对信息请求的响应,我认为在定义诸如rowCountcolumnCount等方法方面没有任何错误。我认为Model是数据源的一种包装(不pipe它是SQL表还是只是一个数组),它以标准forms提供数据,而您应该根据您的数据源结构来定义方法。

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

我感到困惑来自他们的QAbstractModelItem类。 这个类不是一个模型项目,而是一个模型的接口。 为了让他们的视图类与模型接口,他们必须为模型创build一个通用的抽象接口。 但是,模型可以是单个项目,项目列表,项目的2个或更多维度的表格等等; 所以他们的界面必须支持所有这些模型的变化。 不可否认的是,这使得模型项目相当复杂,并且使得它与实际模型一起工作的胶合代码似乎稍微延伸了隐喻。