Announcer
and Announcement
classes provide an implementation of the observer design pattern in Pharo.
As written on the wikipedia page, it aims to address the following problems:
- A one-to-many dependency between objects should be defined without making the objects tightly coupled.
- It should be ensured that, when one object changes state, an open-ended number of dependent objects are updated automatically.
- It should be possible that one object can notify an open-ended number of other objects.
This page provide a quick tutorial to get started with them.
The Announcer
class implements a mechanism to:
- Allow objects (the observers) to subscribe to announcements
- Manage objects subscriptions
- Allow the object holding the announcer (the subject) to announce events
For example, there is an announcer available to let objects listen to announcements (events) concerning the system: SystemAnnouncer uniqueInstance
.
Such announcements include:
ClassAdded
ClassRemoved
MethodModified
- etc.
If one wants to be notified when a class is removed, it is possible to subscribe an object to this specific announcement as follow.
First, let's create the method #whenClassAdded:
for the object that will listen to ClassAdded
announcement.
This method will be the hook called when such announcement has been announced.
It takes the announcement sent by the subject as parameter.
MyObjectListeningToClassAdded>>#whenClassAdded: aClassAdded
Transcript
show: aClassAdded classAffected name;
show: ' has been added.';
cr
Then, we subscribe an instance of our object to the announcer. When doing that, it is needed to specify
- Which kind of announcements (its class) our object listen to.
- Which object listen to this kind of announcements.
- Which method to call when the annoncement is sent.
The previous step can be achieved as follow:
SystemAnnouncer uniqueInstance
when: ClassAdded send: #whenClassAdded: to: instanceOfMyObjectListeningToClassAdded
Note: the SystemAnnouncer is a bit special because it is a subclass of
Announcer
. Usually, when creating you own announcer, you do not subclassAnnouncer
but rather use it directly by storing an instance in a dedicated instance variable (see Section "Using announcer for your subject object").
Once the code above has been executed, a message is printed in the Transcript each time a class has been added.
To unsubscribe an object from an announcer, simply call #unsubscribe:
method on the announcer.
SystemAnnouncer uniqueInstance
unsubscribe: instanceOfMyObjectListeningToClassAdded
Creating a new kind of announcement to fit your needs is easy, just subclass Announcement
class.
For example, let's say you have an object that has a color. You want to allow observers to listen to color changes of the object.
To do that, let's create a ColorChangeAnnouncement
class:
Announcement subclass: #ColorChangeAnnouncement
slots: { #newColor }
classVariables: { }
package: 'MyPackage'
Your new kind of announcement aims to hold all the information you need.
In our example, ColorChangeAnnouncement
hold the new color in #newColor
instance variable.
The common pattern to use an Announcer
in your subject object is to:
- Create an
#announcer
instance variable which will contain the instance ofAnnouncer
. - Create an accessor method for
#announcer
instance variable that use lazy-initialization (so no announcer is created if no announcement is sent and no one wants to subscribe to events of the subject). - The subject object send announcements through its announcer.
As an example, let's implement the object changing its color discussed in previous section"
Object subclass: #MyObjectChangeColor
slots: { #announcer. #color }
classVariables: { }
package: 'MyPackage'
Then we create the accessor for #announcer
with lazy-initialisation.
MyObjectChangeColor>>#announcer
^ announcer ifNil: [ announcer := Announcer new ]
And when the color of the object is updated, we announce it:
MyObjectChangeColor>>#color: aColor
color := aColor.
self announcer
announce: (ColorChangeAnnouncement new
newColor: aColor;
yourself)