Question

This is something that has been bugging me for a bit in a program I am trying to build. It is a desktop GUI application, and I settled on the use of the famous "MVC" (Model-View-Controller) architectural pattern for separating out the GUI and business logic. I've spent many hours wracking my head over various descriptions of this pattern that all seem to either not directly address the desktop use case, or contain a lot of jargon with regard I am not familiar, e.g. this:

https://stackoverflow.com/questions/5863870/how-should-a-model-be-structured-in-mvc/5864000#5864000

was perhaps one of the most interesting articles I found on the topic, but it took me many hours to penetrate because it was written for someone programming PHP in a Web context, which is not my chief area of programming experience. That said, I think I finally understand all the structure of the MVC pattern and could easily give a similar writeup with much less jargon.

However, the devil is in the details, and that's where I'm hung up right now. In particular, I am interested in the relationship between the View and Controller components - most questions on this seem to talk of the relation between the View/Controller and Model, but here it's this relation that's bugging me because as I understand it, Controller objects should not know about View objects and conversely, except in a very abstract sense which basically amounts to them passing messages or events between each other, particularly when seen in light of other design principles like SOLID (esp. the "I"). For example, a Controller object should not know there are things called "Views" out there, but it might know that there is a thing called a NewTweetsReceiver. This tiny interface would, of course, be implemented by a View or perhaps several Views, c.f. Observer pattern.

All this is good so far - but now consider this. It is very common that we have in a desktop application the dynamic creation and destruction of GUI elements. For example (and this is actually where I am at!) when you hit the "New" command in a document editor, the next thing to happen may be that the editor throws a dialog box at the user asking for various initialization parameters for the new document to be created. The question is, though, where should we place the creation of that dialog box? What has the responsibility for that? Because I've heard statements like that "Controllers return Views" which suggests to me the following "back and forth" process:

  1. The user clicks on the "New" button in the application main window (which may be a view or several views in code).
  2. The view that has control over that "New" button tells a controller that the user wants to create a new document.
  3. The controller now produces as a response to that a view which is the dialog box (or could be, at least) asking for information.
  4. The user puts this information into the box and hits the "OK" button. In code, the view now informs the controller of the information entered.

But then it seems that when you do that, the neat walls of separate responsibilities start to erode, as we've effectively mixed together specifics of how the UI is laid out, namely, that it's apportioned between a main window and a dialog box, into the controllers, because the sequencing of events implied and even the creation of that dialog box are now baked into the controller logic. And that doesn't seem kosher: if we decide to change that organization, we have to invade the controller, which we shouldn't have to do so long as we're not adding new functionality, just reorganizing the UI.

So, how should we lay this out to properly preserve the V/C separation in the MVC pattern? The only alternative I can think of is having the main window part spawn the dialog directly, and the Controller only ever knows of a "create new document" as "create a document with such-and-such parameters", completely oblivious to however that stuff is gathered from the user. But if we take the dialog and window to be Views, then is it kosher to have views create each other? Yet I've never heard of that in any material I've found on this topic.

What am I missing here?

Était-ce utile?

La solution

No. Controllers are very well aware of views, directly or indirectly via abstractions such as factories.

That being said they shouldn't be aware of the implementation, just the role that they play, this is often an abstract base class, interface, or some expected duck type depend on the specific language. So in your example...

  • The Controller isn't aware of the view the user interacted with to activate it. It could have been a menu item, a button on a ribbon, a key command (ctrl+n), or an automated macro command from a running script.
  • The Controller is aware that it needs to present a request for more information. That request is the presentation of a view. However it shouldn't get hung up on that being strictly graphical, after all that macro command might be headless (no windows), or you might be porting this for blind users who need an audio equivalent. So the implementation will vary but the purpose, a view to extract information, does not and the controller must be aware of it.

A good way to handle this is for the view to also pass in a ViewFactory when calling controllers, or for the controllers to be created with a ViewFactory before being attached to a view. Eitherway its the same effect, when the controller needs a view it asks the ViewFactory for a view that achieves X.

So strictly speaking controllers do not create views, but they are responsible for determining which view and when to create it.

That being said Controllers don't micro manage views, the view is responsible for reacting to how the model changes, and Controllers manage models. If the view has properties that the model does not (such as being visible, enabled, etc...) the solution is to create a ViewModel (often it composes the model, or extends of the model, but not always). The controller updates the ViewModel, and the View responds to that as well.

The first view though is created by the application startup itself.

Licencié sous: CC-BY-SA avec attribution
scroll top