The model, the view and the presenter are tied together. Unlike the three musketeers which were tied together by friendship and the fight for justice, our three objects are tied together by the dependency mechanism we already discussed in the previous sections.
Earlier we wrote a model does not know about its view(s). However how can a view be notified its model changed?
In the MVC design, a view is added as a dependent object to the model it wants to receive update from.
model addDependent: aView. ... model changed: #color
Then the dependent view receives #update: message with an argument exposing the changed aspect in the model. It must handle it appropriately
view>>update: aspect
aspect == #color ifTrue: [self color: model color] ...
A view can stop listening to a model to not receive #update: message anymore
model removeDependent: aView
There are two drawbacks to this design: all the changed aspects in the model are handled in an unique update: method in each listening view, if there are a lot of aspects to handle, the update: becomes cluttered. Moreover, the update is sent to all the views, independently of there interest for a particular aspect; think about a view not interested in the change of the color aspect of a model, it still receives color update.
The changed/update mechanism appeared with the MVC design, looking at the implementation of the changed: method is interesting
Object>>changed: aspectSymbol
"Receiver changed. The change is denoted by the argument. Usually the argument is a Symbol that is part of the observer's change protocol. Inform all of the observers. Note: In Smalltalk-80 observers were called 'dependents'." self triggerEvent: #changed: with: aspectSymbol
underneath it is implemented with the trigger event we met in the previous section. Indeed, it is now superseded and implemented with the observer pattern which offers more flexibility. We discuss it in the next section.
The changes/update mechanism is still largely used in the Morphic widget, therefore it is worth getting acquainted with.
When several views are tied to an identical model, a given view may only be interested to receive update of a specific aspect of the model.
To do so we specifically register the aspect a view is interested by
model when: #border send: #adjustBorder to: view. model when: #color send: #setColor: to: anotherView
then the model triggers events
model triggertEvent: #border. model triggerEvent: #color with: Color red
and the effects on the views are equivalent to the sent messages
view adjustBorder. anotherView setColor: Color red
The sent message is set at the registering time of the event with the message #when:send:to: and the optional parameter is set when triggering the event with the message #triggerEvent:with:.
An additional flexibility of the observer pattern: it is not required to subclass the view to implement a specific method, as it was necessary with the update: method.
In our memory game, this is one reason we dont’t need to define a card view class, we use the stock PluggableButtonMorph. Observe how we register these events, we previously left out this part (See MemoryGameWindow>>installCards)
installCards
... cardView := PluggableButtonMorph model: presenter action: #flip: actionArgument: x@y. cardModel when: #color send: #color: to: cardView; when: #lock send:#lock to: cardView; when: #unlock send: #unlock to: cardView; when: #flash send: #flash to: cardView. cardView layoutSpec proportionalWidth: 1; proportionalHeight: 1. ...
One last detail, to foster your understanding of the relation between these two event mechanisms, the addDependent: method is also implemented with the observer pattern
Object>>addDependent: anObserver
"Make the given object one of the receiver's observers (dependents). Receivers of this message, i.e. objects being observed, are usually called Models. Observing can also be set up sending one of the #when:send:to: or #addDependent: messages. Models will send themselves #changed:, #changed or one of the #triggerEvent* messages to notify possible observers. If appropriate, it is best to make Models inherit from ActiveModel, for performance reasons." self when: #changed: send: #update: to: anObserver. ^ anObserver
See Memory Game v2 for the complete game code.
We end here our chapter regarding the design of a GUI application. The more you will use this design, the more you will appreciate it, particularly when a project grows.