为什么 Qt 滥用模型/视图术语?
我认为 Qt 中使用模型/视图控件的术语是有缺陷的。在他们的解释页面上,他们声明,他们将 MVC 简化为MV 通过合并视图和控制器,他们给出了以下图片:
但是我认为,他们错误命名了角色对象,我认为,
- 他们所说的带有合并控制器的视图实际上只是一个视图。
- 他们所谓的模型实际上只是控制器。
- 如果你真的想要一个模型,那么它就在他们的“数据”所在的地方。
我说的是在应用程序中使用 Qt 模型/视图组件的通常且理智的方式。 原因如下:
- 这通常是按原样使用的 Qt 组件,没有添加任何特定于您的对象的控制器逻辑)
- 这几乎不是一个模型,只是因为您应该实现几个 Qt 方法,如 rowCount、columnCount、data 等。与你的模型无关。事实上,控制器中存在典型的模型方法。当然,您可以在这里实现控制器和模型逻辑,但首先这将是非常糟糕的代码设计,其次您将合并控制器和模型,而不是它们所说的控制器和视图。
- 正如原因2中所说,如果你想分离模型逻辑,它肯定不是图片上的蓝色框,而是虚线的“数据”框(当然是与真实数据通信)。
Qt他们的术语是错误的,还是只有我不明白? (顺便说一句:这不是学术问题的原因是我已经开始按照他们的命名来编写我的项目,我很快就发现,代码显然是不正确的。直到那之后我才意识到,我应该不要尝试将模型逻辑放入他们所谓的模型中)
I think that the terminology used in Qt with model/view controls is flawed. On their explanation page they state, that they simplified the MVC to MV by merging View and Controller and they are giving the following picture:
However I think, they misnamed the roles of objects and I think that,
- What they call View with merged Controller is in fact a View only.
- What they call Model is in fact Controller only.
- If you really want to have a model it would be somewhere where their "Data" is.
I am speaking about usual and sane way you would use Qt model/view component in your app.
Here are the reasons:
- This is typically Qt component which is used as is, without adding any Controller logic specific to your objects)
- This is hardly a Model, just because you should implement several Qt methods like rowCount, columnCount, data etc. which have nothing to do with your model. In fact there are typical model methods found in Controllers. Of course, you can implement both Controller and Model logic here, but first it would be quite bad code design and secondly you would merge Controller and Model not Controller and View as they state.
- As said in reason 2. if you want to separate Model logic that it is surely not the blue box on the picture, but rather the dashed "Data" box (communicating to real Data of course).
Is Qt wrong in their terminology, or it is just me who does not understand? (BTW: The reason why it is not academic question is that I have started to code my project following their naming and I have soon found out, that the code clearly is not right. It was only after that when I realized, that I should not try put Model logic in what they call Model)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
简短回答
Qt 的 MVC 仅适用于一种数据结构。当谈论 MVC 应用程序时,您不应该想到 QAbstractItemModel 或 QListView。
如果您想要整个程序采用 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,适应我们自己的需求。Model
和View
类本身与 Qt MVC 没有任何关系。我们把逻辑放在哪里?我们创建了一些类,通过从源模型读取数据(当它们发生变化时)并将结果写入目标模型来对数据进行实际计算。从 Qt 的角度来看,该逻辑类将是视图,因为它们“连接”到模型(不是用户的“视图”,而是应用程序的业务逻辑部分的“视图”)。
控制器在哪里?在最初的 MVC 术语中,控制器解释用户输入(鼠标和键盘)并向模型发出命令以执行请求的操作。由于 Qt 视图已经解释了用户输入,例如重命名和移动项目,因此不需要这样做。但我们需要的是超越 Qt 视图的用户交互解释。
Short answer
Qt's MVC only applies to one data structure. When talking about an MVC application you should not think about
QAbstractItemModel
orQListView
.If you want an MVC architecture for your whole program, Qt hasn't such a "huge" model/view framework. But for each list / tree of data in your program you can use the Qt MVC approach which indeed has a controller within its view. The data is within or outside of the model; this depends on what type of model you are using (own model subclass: probably within the model; e.g. QSqlTableModel: outside (but maybe cached within) the model). To put your models and views together, use own classes which then implement the business logic.
Long answer
Qt's model/view approach and terminology:
Qt provides simple views for their models. They have a controller built in: selecting, editing and moving items are something what in most cases a controller "controls". That is, interpreting user input (mouse clicks and moves) and giving the appropriate commands to the model.
Qt's models are indeed models having underlying data. The abstract models of course don't hold data, since Qt doesn't know how you want to store them. But you extend a QAbstractItemModel to your needs by adding your data containers to the subclass and making the model interface accessing your data. So in fact, and I assume you don't like this, the problem is that you need to program the model, so how data is accessed and modified in your data structure.
In MVC terminology, the model contains both the data and the logic. In Qt, it's up to you whether or not you include some of your business logic inside your model or put it outside, being a "view" on its own. It's not even clear what's meant by logic: Selecting, renaming and moving items around? => already implemented. Doing calculations with them? => Put it outside or inside the model subclass. Storing or loading data from/to a file? => Put it inside the model subclass.
My personal opinion:
It is very difficult to provide a good and generic MV(C) system to a programmer. Because in most cases the models are simple (e.g. only string lists) Qt also provides a ready-to-use QStringListModel. But if your data is more complex than strings, it's up to you how you want to represent the data via the Qt model/view interface. If you have, for example, a struct with 3 fields (let's say persons with name, age and gender) you could assign the 3 fields to 3 different columns or to 3 different roles. I dislike both approaches.
I think Qt's model/view framework is only useful when you want to display simple data structures. It becomes difficult to handle if the data is of custom types or structured not in a tree or list (e.g. a graph). In most cases, lists are enough and even in some cases, a model should only hold one single entry. Especially if you want to model one single entry having different attributes (one instance of one class), Qt's model/view framework isn't the right way to separate logic from user interface.
To sum things up, I think Qt's model/view framework is useful if and only if your data is being viewed by one of Qt's viewer widgets. It's totally useless if you're about to write your own viewer for a model holding only one entry, e.g. your application's settings, or if your data isn't of printable types.
How did I use Qt model/view within a (bigger) application?
I once wrote (in a team) an application which uses multiple Qt models to manage data. We decided to create a
DataRole
to hold the actual data which was of a different custom type for each different model subclass. We created an outer model class calledModel
holding all the different Qt models. We also created an outer view class calledView
holding the windows (widgets) which are connected to the models withinModel
. So this approach is an extended Qt MVC, adapted to our own needs. BothModel
andView
classes themselves don't have anything to do with the Qt MVC.Where did we put the logic? We created classes which did the actual computations on the data by reading data from source models (when they changed) and writing the results into target models. From Qt's point of view, this logic classes would be views, since they "connect" to models (not "view" for the user, but a "view" for the business logic part of the application).
Where are the controllers? In the original MVC terminology, controllers interpret the user input (mouse and keyboard) and give commands to the model to perform the requested action. Since the Qt views already interpret user input like renaming and moving items, this wasn't needed. But what we needed was an interpretation of user interaction which goes beyond the Qt views.
我同意你的观点,Qt 的命名具有误导性。然而,在我看来,问题不仅仅是 Qt 的问题,而是所有允许我们遵守
如果您比较术语“模型”和“视图”在这些框架中的使用方式,以及“视图”中的类的职责,“模型” ”,和“Controller”(如果有的话)有了,你会发现有很大的区别。比较不同的概念和术语肯定是有用的,这样人们从一种框架切换到另一种框架就有机会保持理智,但这需要大量的工作和研究。 Martin Fowler 的概述值得一读。
由于 MVC 模式的外观有很多不同的想法,哪一个是正确的?在我看来,当我们想知道如何“正确”实现 MVC 时,应该向发明 MVC 的人求助。在原始的smalltalk论文中,它说:
鉴于此,我将回答您的三个主要问题:
它把我们留在哪里?在我看来,最好弄清楚当使用术语“模型”和“视图”时 Qt 的真正含义,并在使用 Qt 编程时以它们的方式使用这些术语。如果你一直被打扰,它只会减慢你的速度,而 Qt 中的设置方式确实允许优雅的设计 - 这比它们的“错误”命名约定更重要。
I agree with you that Qt's naming is misleading. In my opinion however, the problem is not Qt's alone, but is shared by all frameworks that allow us to adhere to the principle of separation of concerns when implementing our UIs. When someone comes up with such a framework, and finds a good way to keep "things" separated, they always feel obliged to have modules that they call "Model" and others that they call "View". Over the years I have worked with these frameworks:
If you compare how the terms "Model" and "View" are used in these frameworks, and what responsibilities the classes in the "View", the "Model", and the "Controller" (if there is one) have, you will find that there are very big differences. It would certainly be useful to have a comparison of the different concepts and terminologies, so that people switching from one framework to another have a chance to stay sane, but that would require a lot of work and research. A good read is Martin Fowler's overview.
Since there are so many different ideas what an MVC pattern can look like, which one is correct? In my opinion, the people who invented MVC should be turned to when we want to know how it is supposed to be implemented "correctly". In the original smalltalk paper it says:
In light of that I would answer your three main concerns thusly:
Where does it leave us? In my opinion, it is best to figure out what Qt really means when the terms "Model" and "View" are used and use the terms in their manner while we are programming with Qt. If you keep being bothered it will only slow you down, and the way things are set up in Qt does allow elegant design - which weighs more that their "wrong" naming conventions.
术语没有对错之分,没有有用或无用之分。
您可能会稍微改变一下问题并询问为什么 Qt 不更适合 MVC。答案是,早期的 Qt 开发人员认为,在 GUI 应用程序中将 V 与 C 解耦会导致 V 和 C 都不好。 QWidget 的设计试图简化将鼠标输入交互与像素输出决策紧密结合的过程,您可以看到这不是通向 MVC 的道路。
The terminology isn't right or wrong, it's useful or useless.
You might change the question a bit and ask why Qt isn't more MVC-friendly. The answer to is that the early Qt developers believe that decoupling V from C in GUI applications makes for bad Vs and Cs both. QWidget's design tries to make it simple to bind mouse input interperation closely with pixel output decisions, and you can see how that's not the road towards MVC.
由于Model的功能是响应信息请求,所以我认为定义rowCount、columnCount等方法没有什么错。我认为Model是某种包装器对于数据源(无论是SQL表还是数组),它以标准形式提供数据,您应该根据您的数据源结构来定义方法。
As Model function is to respond to requests for information, I think there is nothing wrong in defining such methods as
rowCount
,columnCount
, etc. I think Model is some kind of wrapper for data source (no matter what is it SQL table or just an array), it provides data in standard form, and you should to define methods depends on your data source structure.我相信他们的术语是正确的......尽管在实际应用程序中我发现根据您的抽象级别很容易模糊模型、视图和控制器之间的界限:一个级别的视图可能是更高级别的模型。
我觉得困惑来自于他们的 QAbstractModelItem 类。此类不是模型项,而是模型的接口。为了使他们的视图类与模型交互,他们必须为模型创建一个通用的抽象接口。然而,模型可以是单个项目、项目列表、包含 2 个或更多维度项目的表格等;因此他们的界面必须支持所有这些模型变体。不可否认,这使得模型项相当复杂,并且使其与实际模型一起工作的粘合代码似乎确实有点延伸了这个隐喻。
I believe their terminology is correct...although in real applications I find it can be very easy to blur the lines between model, view, and controller depending on your level of abstraction: one level's view may be a higher level's model.
I feel the confusion arises from their QAbstractModelItem class. This class isn't a model item, but rather it is an interface to a model. To make their view classes interface with the model, they had to create a generic abstract interface to the model. However, a model can be a single item, a list of items, a table of 2 or more dimensions of items, etc; so their interface has to support all these model variations. Admittedly, this makes the model items fairly complex, and the glue code to make it work with an actual model does seem to stretch the metaphor a bit.
不,他们的“模型”绝对不是控制器。
控制器是修改模型(因此间接修改视图)的用户可见控件的一部分。例如,“删除”按钮是控制器的一部分。
我认为经常会出现混淆,因为许多人看到“控制器修改模型”之类的内容,并认为这意味着模型上的变异函数,例如“deleteRow()”方法,但在经典 MVC 中,控制器专门是用户界面部分。改变模型的方法只是模型的一部分,
自从 MVC 发明以来,控制器和视图之间的区别就变得越来越紧张:它既显示一些文本又允许您编辑它,视图也是如此。答案一定是它是两者的一部分,当您在 20 世纪 60 年代开发电传打字机时,区别就更清楚了 - 想想
ed
– 但这并不意味着当时的用户情况会更好!确实,他们的 QAbstractItemModel 比模型通常的级别要高得多。例如,其中的项目可以有背景颜色(技术上是画笔),这绝对是一个视图属性!因此,有一种观点认为 QAbstractItemModel 更像是视图,而您的数据是模型。事实上,它介于视图和模型的经典含义之间。但我不明白它是如何成为一个控制器的;如果有的话,那就是使用它的 QT 小部件。
No, their "model' is definitely not a controller.
The controller is the part of user visible controls that modify the model (and therefore indirectly modify the view). For example, a "delete" button is part of the controller.
I think there is often confusion because many see something like "the controller modifies the model" and think this means the mutating functions on their model, like a "deleteRow()" method. But in classic MVC, the controller is specifically the user interface part. Methods that mutate the model are simply part of the model.
Since MVC was invented, its distinction between controller and view has become increasingly tense. Think about a text box: it both shows you some text and lets you edit it, so is it view or controller? The answer has to be that it is part of both. Back when you were working on a teletype in the 1960s the distinction was clearer – think of the
ed
– but that doesn't mean things were better for the user back then!It is true that their QAbstractItemModel is rather higher level than a model would normally be. For example, items in it can have a background colour (a brush technically), which is a decidedly view-ish attribute! So there's an argument that QAbstractItemModel is more like a view and your data is the model. The truth is it's somewhere in between the classic meanings of view and model. But I can't see how it's a controller; if anything that's the QT widget that uses it.