Model/View/Presenter and Qt?
Here’s a question for Qt hackers:
Does Qt implement the Model/View/Presenter design pattern?
The short answer is (drumroll): it depends on some stuff. Let’s dive into it and give you a real answer.
First of all, I am no Model/View/Presenter expert. I’ve only read what little information is available online, and I’ve never implemented an application with it (though it turns out that I’ve been using it for years on accident). Bottom line: Please correct me.
- It is a multi-tiered design pattern that helps separate display logic from business logic in a testable way.
- The View is kept as thin and ignorant as possible
- It enables better automated unit testing (and helps prevent relying on awkward UI test runners)
- It moves lots of logic out of the View and into the Presenter (things like input validation for example)
- The Presenter should be common to multiple views (though I’m not totally sure on this one — please correct me)
- Methods in the Presenter should not return a value (i.e., void) and should take no arguments.
Let’s look at a typical Qt application. I like to use Qt Designer to design as much of my user interface as possible. Doing so, my source code tends to look like this:
Qt veterans should be able to identify the boxes in this graphic quickly, but for those of you who don’t work in Qt every day, let me explain.
Your Data Model
The data model is a class (or set of classes) that provides access to your app’s data. It emits signals to tell you when its data has changed (like if another app changed the data), and provides slots you can call to change the data yourself. You may have, for example, a list of User objects in your app, and maybe they are all contained within a UserModel class. When a user’s phone number changes, your model may emit a signal called phoneNumberChanged(). If a new user is created, the UserModel would emit something like userAdded(User*) and likewise userRemoved(User*) when a user goes away.
Your Widget Subclass
When using Designer, you have to choose a QWidget-derived class from which your widget will inherit. Your widget may be a dialog, a main window, or even a nested widget inside some scroll box. Your widget subclass will usually connect to the data model’s signals (userAdded(), userRemoved(), and friends). Its job is to notice when the data model changes, and represent that on the screen by manipulating the view. It is also responsible for taking user input and pushing it into the model (like when the user wants to change a User‘s phone number, for example).
Generated UI Class
When you create and save a form in Qt Designer, it gets saved with a .ui extension. This file actually contains a bunch of XML that describes how you laid stuff out on the screen. Qt provides a tool called uic that takes that XML file and produces C++. uic stands for User Interface Compiler. The code that it generates is the view. It only does one thing: Draw crap on the screen, and tell you when the user does stuff. It is an extremely thin UI layer. So thin in fact, that it cannot implement any business logic at all. It’s simply not possible. The code is generated and unalterable (unless you’re an idiot and think it’s okay to go hack that code after it’s generated — hey, I’ve done stupid stuff too).
Now let’s apply some labels to our diagram:
I believe the widget subclass actually does conform to all the rules of a Presenter, even though it doesn’t conventionally bear that name. Let’s look at the rules again and see if it fits the bill:
- Separates display logic from business logic: Yes
- The View is thin and ignorant: Yes
- Enables automated unit testing: Yes (you can write unit tests against your widget subclass quite easily)
- It moves UI-related logic out of the View into the Presenter: Yes In fact, the View can do almost no input validation at all (unless you specify inputMasks in Designer, and note that QValidators are instantiated in the widget subclass).
- The Presenter should be common to multiple views: No Qt could allow this, but most people just don’t do it (perfectly possible, but I’ll discount it because it is not used in practice or recommended in the docs).
- Methods in the Presenter should not return a value: Yes Your widget subclass’s slots have to be void (and even if you do specify a return value, Qt ignores it anyway, so Qt actually enforces this requirement).
Qt can most definitely implement Model/View/Presenter, and in fact, the Qt documentation recommends exactly that, though they never use the term “Presenter”. You should probably also read up on the Model/View/Presenter’s latest development, since some argue that the term ought to be retired and replaced with new terminology, which coincidentally fits even better with Qt conventions.
What do you think? Does Qt actually implement this Design Pattern? It’s pretty important to recognize this stuff, or we can end up screwing up pretty bad.