|
Spot the difference!
What's the difference between these two figures?
Which one do you think is the correct one? Which one do you prefer?
Well, if we try to follow the Model-View-Controller paradigm,
then:
Controller: This responds to events, typically user actions, and invokes changes on the model (but *not* the view).
So it seems that the second one is the correct one. It's the model that notifies
the view about any change, and not the controller handling the view directly. (Of course
that's the way things are done in Swing, so that approach is possibly more intuitive than
the previous one, right?).
So I propose
being the model that pushes events into the bus whenever it is changed. The model
should drive the bus. Actuators will
then receive these changes and update the views accordingly.
The flow of events would then be as follows:
- 1. Macro-components generate events, that travel the event bus using topics.
- 2. Controllers receive and coordinate these events, and modify the model accordingly.
- 3. The model fires events when changed, and these travel through the bus using
(one or more?) topics.
- 4. The actuators are updated of changes and update the view accordingly.
Opinions on this? What do you think?
Since I'm afraid this is getting too academic let's see things by example.
Opening files
Let's assume the user wants to open a file.
- 1.
The user
selects "File/Open" in the menu (or in a toolbar) and an ActionEvent is generated from the
menubar (toolbar) through the "menubar.events" or "toolbar.events" topics in the bus.
- 2. A controller is connected to both the "menubar.events" and "toolbar.events" topics and
asynchronously reads the file and creates a javax.swing.text.Document for the file.
- 3. The controller updates the model inserting this javax.swing.text.Document in the model (possibly in
the Swing thread). Once the model is updated it generates a DocumentOpenedEvent event through the
"model.documents" topic in the bus.
- 4. There're different actuators listening on this "model.documents". The "MenubarActuator" will
insert an entry in the "Recently opened" menu. The "EditorActuator" will open a new
editor tab and include the new javax.swing.text.Document in an EditorPane. The "StatusBarActuator"
will update the message in the status bar indicating that the file was opened.
Note: if there's an IOException in step 2 above then steps 3 and 4 are not executed, but instead:
- 3. The controller updates the model inserting an Exception in the model's "errors" list.
Once the model is updated the model itself generates a "ApplicationErrorEvent" through the
"application.errors" topic in the bus.
- 4. There're different actuators listening on this "application.errors" topic. The StatusBarActuator,
for instance, will show a red label with the error.
Note there's no veto here: the controller detects the error and generates an event.
Another example: closing the application
- 1. The user selects "File/Exit" in the menu (or clicks on the close window button in the window) and
an ActionEvent is generated from the menubar (or a WindowEvent is generated). These events
travel through the "menubar.events" or the "window.events" topics in the bus.
- 2. A controller is connected to both the "menubar.events" and "window.events" topics in the bus.
- The controller checks the model to see if all files are closed. If not then it will ask the user (in the event thread) if he wants to quit without closing. If the user selects "YES" then the controller updates the model indicating that the application has to be closed. If the user selects "NO" then the controller does nothing.
- 3. If the application has to be closed then the model fires an "ApplicationQuit" event through the "application.control" topic.
- 4. The "ApplicationActuator" releases all resources used by the views, closes and disposes all windows.
Just one more example before continuing...
Another example: saving
Let's assume the user wants to save a file.
- 1. The user selects "File/Save" in the menu (or clicks the save button in the toolbar).
An ActionEvent is generated from the menubar (or an ActionEvent is generated in the toolbar).
These events travel through the "menubar.events" or the "toolbar.events" topics in the bus.
li>2. A controller is connected to both the "menubar.events" and "toolbar.events"
topics in the bus.
- The controller asynchronously saves the document (contained in a javax.swing.text.Document in
the model). And then updates the model indicating that the document is saved.
- 3. When the document in the model is saved an "DocumentSavedEvent" is sent through the "model.documents" topic.
- 4. There're different actuators listening on this "model.documents" topic. The "MenubarActuator" will disable the save menu (because the document is saved). The "StatusBarActuator"
will set a message indicating that the document was saved.
What about errors? If there's an error saving the document in step 2 then the model
won't be updated and steps 3 and 4 above will not be executed. The behaviour could be
similar to the one when opening the file above.
Vetoing and threading
Note that in the examples above there's no need to veto events: it's controllers who are
responsible for handling errors and unsaved situations, so all the hassle about handling
vetoing in different threads just dissappears. Again, I can't think of a situation where
vetoing is strictly needed (any ideas here would be appreciated).
I think we need more brainstorming for the threading issues. Let's say we accept
the model as the bus driver. The one that rules actuators. Then that's probably a
good hint on how threading should work. The model should be modified by a single
thread, I suggest. So probably the easiest thing to do is to use the EDT as the
one responsible for modifying the model. So all events to actuators should be
genereated in the EDT. That's probably the easiest thing to do, right?
State machines
I keep on thinking of state machines in the background. Sorry I can't resist!
Interesting things I've found about state machines for GUIs and that need further
investigation:
I think the next step could be to modify the demo application to make the model drive the bus. Don't you think?
Thanks for any suggestions,
Antonio
|
Enviado por codecraig en octubre 03, 2005 a las 09:35 PM CEST #
Enviado por Nascif A. Abousalh Neto en octubre 04, 2005 a las 04:10 PM CEST #
If your File/Open example has to Save the file and remove the View before Opening another file (as an example), your controller would have to wait until Save was finished in order to Open the file. In the event of an error in Save you would be waiting on an event (either error or model). This means that your commands would have to be synchronous, or able to be synchronous, or at least you would have to wait for an event to be generated after issuing the command before you could proceed. This would end up acting like a Veto. Wouldn't it be better to hide that waiting inside the Bus or support object that fires the event? Or just have vetoes?
Also, for the delivery of events the support object or Bus could be set up with a strategy for delivering event (i.e. on the event dispatch thread). This would eliminate the user knowing the delivery method of an event. You could have four types of delivery mechanisms (SYNC, ASYNC, SYNC_SWING, ASYNC_SWING). They could vary from command event to state event. For example a File/Open command event could be guaranteed to be SYNC_SWING while a File/Opened event could be guaranteed to be ASYNC_SWING or ASYNC.
Your model example seems very close to an Application context like some apps have. I like it!.
P.S. I have to agree with craig...A code example would help...:-)
Tim
Enviado por Tim Osten en octubre 07, 2005 a las 08:11 PM CEST #
But why and where do you want to use Annotations?
(I read some time ago, they aren't real meta data and have some disadvantages. Sorry, i can't remember where i read that some time ago.)
Enviado por Ingo Siebert en octubre 10, 2005 a las 09:10 AM CEST #