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

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

picture explaining Qt MVC

然而我认为,他们错误地命名了物体的角色,我认为,

  1. 他们所谓的视图与合并控制器实际上只是一个视图。
  2. 他们所谓的模型实际上只是控制器。
  3. 如果你真的想有一个模型,它将是他们的“数据”的地方。

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

  1. 这是典型的 Qt 组件,不需要向对象添加任何特定于 Controller 的逻辑)
  2. 这几乎不是一个模型,因为您应该实现几个 Qt 方法,如 rowCount、 columnCount、 data 等,这些方法与您的模型没有任何关系。事实上,控制器中有典型的模型方法。当然,您可以在这里同时实现 Controller 还有模型逻辑,但首先,这将是相当糟糕的代码设计,其次,您将合并 Controller 和 Model,而不是 Controller 和 View。
  3. 正如理由2所说。如果您想分离模型逻辑,那么它肯定不是图片上的蓝色框,而是虚线“数据”框(当然是与实际数据通信)。

是 Qt 在他们的术语中是错误的,还是只是我不明白?(顺便说一句,这不是学术问题的原因是,我已经开始编写我的项目后,他们的命名和我很快发现,代码显然是不正确的。只有在那之后,我才意识到,我不应该尝试把模型逻辑放到他们所谓的模型中)

36276 次浏览

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.

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.

Short answer

Qt's MVC only applies to one data structure. When talking about an MVC application you should not think about QAbstractItemModel or QListView.

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 called Model holding all the different Qt models. We also created an outer view class called View holding the windows (widgets) which are connected to the models within Model. So this approach is an extended Qt MVC, adapted to our own needs. Both Model and View 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.

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:

  • MFC
  • Qt
  • Swing
  • SWT
  • WPF with MVVM

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:

The view manages the graphical and/or textual output to the portion of the bitmapped display that is allocated to its application. The controller interprets the mouse and keyboard inputs from the user, commanding the model and/or the view to change as appropriate. Finally, the model manages the behavior and data of the application domain, responds to requests for information about its state (usually from the view), and responds to instructions to change state (usually from the controller).

In light of that I would answer your three main concerns thusly:

  1. In fact a Qt component "manages the graphical [...] output", and "interprets the mouse and keyboard inputs", so it could indeed be called merged View and Controller with respect to the definition above.
  2. I agree that you are/would be forced to merge Controller and Model (again with respect to the definition above).
  3. I agree, again. The Model should only manage the data of the application domain. This is what they call "data". Clearly, dealing with rows and columns for example has normally nothing to do with our applications domain.

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.

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.

I think that ... What they call Model is in fact Controller only.

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.