You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

112 lines
6.5 KiB

PUBLISHER
=========
PUBLISHER is a tclOO-class providing a general facility for implementing
the publisher-subscribers pattern.
A publisher is an object usually attached to a master-object (e.g. a complex data structure such a list, a graph, a table, a database ...) that can raise events of public interest.
These events usually occur when something in the master-object changes.
The master-object knows nothing about its potentials subscribers; it simply tells the publisher to notify some events to all the (dynamically) registered subscribers.
Subscribers (also referred as observers) are clients interested about changes occurring on that particular master-object. They inform the publisher they are interested on some events and give it a callback, i.e. a command that publisher should call every time events are generated.
Events may have parameters that will be passed to subscribers' callbacks.
RECOMMENDED NAMING CONVENTIONS
------------------------------
These naming conventions are not mandatory. You are encouraged to follow them - or any other coherent set of conventions - just because they help to write a more readable code (without the need of extra "comments").
* Each *event-name* has a leading "!" (e.g !ev1 !alert ...)
* Each event may carry details (*event's data*) as a list of key-values; Keys have a leading "-" (e.g. -color blue -temperature 100.0 .... )
* Each different *subscriber* must be identified by a different "id".
If subscriber is a widget, "id" could be the widget-name
If subscriber is a (snit) object, "id" could be its name
Otherwise, you should choose an "id", provided it is unique among all possible subscribers (of the same publisher)
* Each *callback* name should be "On!event" (e.g. On!alert )
How to interact with a publisher (subscribers-side)
--------------------------------------------------
Let's suppose we have a publisher ($pub) attached to a master-object and some observers wishing to be informed whenever a change in the master-object occur.
The folllowing command lists all the possible events
$pub events ;# --> { !configure !alert !full !evXYZ !destroyed }
Then an observer (whose "id" is "$obsA") interested on event "!evXYZ" should
1) setup a callback
proc On!evXYZ {args} {
array set param $args
.. do something with param(-color), param(-x) param(-y) ....
}
2) tell the publisher to call its callback for all the next notifications.
$pub register !evXYZ $obsA On!evXYZ
Of course each different event may provide different details (event-data), and it is the subscriber's responsibility to setup a conformant callback.
When an observer ($obsA) is no more interested in publisher's notifications it must revoke the subscription
$pub unregister !evXYZ $obsA ;# revoke subscription for event !evXYZ
or
$pub unregister * $obsA ;# revoke ALL its-own subscriptions
Note that an observer MUST revoke ALL its subscriptions before being destroyed, or the publisher will send all next events to a no-more-existing client.
** Universal callback **
For testing purpose with just few lines of code, you can setup an universal callback, able to print every detail, catching all possible events:
# Universal-callback; note the first two "fixed" parameters ...
proc On!EveryEvent {ev pub args} {
puts "event ($ev) from ($pub)"
foreach {key val} $args {
puts "\tkey: ($key) -- ($val)"
}
}
# register for all events
foreach ev [$pub events] {
$pub register $ev $obsA [list On!EveryEvent $ev $pub]
}
Note that the first two parameters of the callback are fixed at "register-time"; the publisher only "appends" events-data (as the usual key-value list) to a command with two "pre-fixed" parameters.
How to interact with subscribers (publisher--side)
--------------------------------------------------
When a master-object needs to interact with several observers, it must create its own publisher for handling such interactions.
First, master-object creates a publisher:
set pub [publisher new]
or
set pub [publisher create _name_]
Then it declares the names of events it will provide
$pub declare !evA !evB !evC
or
$pub declare !evA ; $pub declare !evB !evC
Other than the explicitelly declared event-names, all publishers always provide a standard event named "!destroyed" informing the subscribers that it has been .. destroyed (usually by the master-object).
This standard event doesn't need to be declared.
The !destroyed event carries no event-data.
Note that (currently) when declaring events there is no way to declare the parameters (event-data).
It's just a matter of good documentation practice: each publishers should document all its events, their meanings, and their parameters.
*** It is strongly recommended that parameters always be transmitted as an unordered list of key-value pairs.
This key-value convention allows to upgrade the publisher-part ( e.g. adding a parameter "-speed" for a given event), without the need to rewrite the previous subscribers callbacks. (Of course non-upgraded subscribers will simply ignore the new parameter). ***
When a change in the master-object occur, the master-object must tell to its publisher to notify the event.
$pub notify !itemRemoved -id 42342
$pub notify !itemAdded -id 12312 -parent 1239866
$pub notify !itemConfigured -id 23452 -color blue -rank "A"
Note that master-object knows nothing about its currently registered subscribers; that's the publisher's job!
When a master-object deletes its publisher, the publisher implicitely trasmits a last event
$pub notify !destroyed
Subscribers should simply 'forget' the publisher, without the need to unregister their callback (in fact, they can't unregister, because there is no publisher to contact!)
=== Publisher-Subscribers vs. Tk-events =======================================
The main difference to the event system built into the Tcl/Tk core is that the latter can generate only virtual events, and only for Tk-widgets.
It is not possible to use the builtin facilities to bind to events on arbitrary (non-Tk-)objects, nor is it able to generate events for such.
Moreover, even for widgets, the bind-event system is rather clumsy when multiple callbacks should be independently attached (bind) to an event, and indipendently detached.
The publisher-subscribers system can be used in a coherent way both for Tk-widgets and for arbitrary objects.