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
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.
|
|
|