1Class::Observable(3)  User Contributed Perl Documentation Class::Observable(3)
2
3
4

NAME

6       Class::Observable - Allow other classes and objects to respond to
7       events in yours
8

SYNOPSIS

10         # Define an observable class
11
12         package My::Object;
13
14         use parent qw( Class::Observable );
15
16         # Tell all classes/objects observing this object that a state-change
17         # has occurred
18
19         sub create {
20            my ( $self ) = @_;
21            eval { $self->_perform_create() };
22            if ( $@ ) {
23                My::Exception->throw( "Error saving: $@" );
24            }
25            $self->notify_observers();
26         }
27
28         # Same thing, except make the type of change explicit and pass
29         # arguments.
30
31         sub edit {
32            my ( $self ) = @_;
33            my %old_values = $self->extract_values;
34            eval { $self->_perform_edit() };
35            if ( $@ ) {
36                My::Exception->throw( "Error saving: $@" );
37            }
38            $self->notify_observers( 'edit', old_values => \%old_values );
39         }
40
41         # Define an observer
42
43         package My::Observer;
44
45         sub update {
46            my ( $class, $object, $action ) = @_;
47            unless ( $action ) {
48                warn "Cannot operation on [", $object->id, "] without action";
49                return;
50            }
51            $class->_on_save( $object )   if ( $action eq 'save' );
52            $class->_on_update( $object ) if ( $action eq 'update' );
53         }
54
55         # Register the observer class with all instances of the observable
56         # class
57
58         My::Object->add_observer( 'My::Observer' );
59
60         # Register the observer class with a single instance of the
61         # observable class
62
63         my $object = My::Object->new( 'foo' );
64         $object->add_observer( 'My::Observer' );
65
66         # Register an observer object the same way
67
68         my $observer = My::Observer->new( 'bar' );
69         My::Object->add_observer( $observer );
70         my $object = My::Object->new( 'foo' );
71         $object->add_observer( $observer );
72
73         # Register an observer using a subroutine
74
75         sub catch_observation { ... }
76
77         My::Object->add_observer( \&catch_observation );
78         my $object = My::Object->new( 'foo' );
79         $object->add_observer( \&catch_observation );
80
81         # Define the observable class as a parent and allow the observers to
82         # be used by the child
83
84         package My::Parent;
85
86         use strict;
87         use parent qw( Class::Observable );
88
89         sub prepare_for_bed {
90             my ( $self ) = @_;
91             $self->notify_observers( 'prepare_for_bed' );
92         }
93
94         sub brush_teeth {
95             my ( $self ) = @_;
96             $self->_brush_teeth( time => 45 );
97             $self->_floss_teeth( time => 30 );
98             $self->_gargle( time => 30 );
99         }
100
101         sub wash_face { ... }
102
103
104         package My::Child;
105
106         use strict;
107         use parent qw( My::Parent );
108
109         sub brush_teeth {
110             my ( $self ) = @_;
111             $self->_wet_toothbrush();
112         }
113
114         sub wash_face { return }
115
116         # Create a class-based observer
117
118         package My::ParentRules;
119
120         sub update {
121             my ( $item, $action ) = @_;
122             if ( $action eq 'prepare_for_bed' ) {
123                 $item->brush_teeth;
124                 $item->wash_face;
125             }
126         }
127
128         My::Parent->add_observer( __PACKAGE__ );
129
130         $parent->prepare_for_bed # brush, floss, gargle, and wash face
131         $child->prepare_for_bed  # pretend to brush, pretend to wash face
132

DESCRIPTION

134       If you have ever used Java, you may have run across the
135       "java.util.Observable" class and the "java.util.Observer" interface.
136       With them you can decouple an object from the one or more objects that
137       wish to be notified whenever particular events occur.
138
139       These events occur based on a contract with the observed item. They may
140       occur at the beginning, in the middle or end of a method. In addition,
141       the object knows that it is being observed. It just does not know how
142       many or what types of objects are doing the observing. It can therefore
143       control when the messages get sent to the obsevers.
144
145       The behavior of the observers is up to you. However, be aware that we
146       do not do any error handling from calls to the observers. If an
147       observer throws a "die", it will bubble up to the observed item and
148       require handling there. So be careful.
149

USER GUIDE

151       Throughout this documentation we refer to an 'observed item' or
152       'observable item'. This ambiguity refers to the fact that both a class
153       and an object can be observed. The behavior when notifying observers is
154       identical. The only difference comes in which observers are notified.
155       (See "Observable Classes and Objects" for more information.)
156
157   Observable Classes and Objects
158       The observable item does not need to implement any extra methods or
159       variables. Whenever it wants to let observers know about a state-change
160       or occurrence in the object, it just needs to call
161       "notify_observers()".
162
163       As noted above, whether the observed item is a class or object does not
164       matter -- the behavior is the same. The difference comes in determining
165       which observers are to be notified:
166
167       •   If the observed item is a class, all objects instantiated from that
168           class will use these observers. In addition, all subclasses and
169           objects instantiated from the subclasses will use these observers.
170
171       •   If the observed item is an object, only that particular object will
172           use its observers. Once it falls out of scope then the observers
173           will no longer be available. (See "Observable Objects and DESTROY"
174           below.)
175
176       Whichever you chose, your documentation should make clear which type of
177       observed item observers can expect.
178
179       So given the following example:
180
181        BEGIN {
182            package Foo;
183            use parent qw( Class::Observable );
184            sub new { return bless( {}, $_[0] ) }
185            sub yodel { $_[0]->notify_observers }
186
187            package Baz;
188            use parent qw( Foo );
189            sub yell { $_[0]->notify_observers }
190        }
191
192        sub observer_a { print "Observation A from [$_[0]]\n" }
193        sub observer_b { print "Observation B from [$_[0]]\n" }
194        sub observer_c { print "Observation C from [$_[0]]\n" }
195
196        Foo->add_observer( \&observer_a );
197        Baz->add_observer( \&observer_b );
198
199        my $foo = Foo->new;
200        print "Yodeling...\n";
201        $foo->yodel;
202
203        my $baz_a = Baz->new;
204        print "Yelling A...\n";
205        $baz_a->yell;
206
207        my $baz_b = Baz->new;
208        $baz_b->add_observer( \&observer_c );
209        print "Yelling B...\n";
210        $baz_b->yell;
211
212       You would see something like
213
214        Yodeling...
215        Observation A from [Foo=HASH(0x80f7acc)]
216        Yelling A...
217        Observation B from [Baz=HASH(0x815c2b4)]
218        Observation A from [Baz=HASH(0x815c2b4)]
219        Yelling B...
220        Observation C from [Baz=HASH(0x815c344)]
221        Observation B from [Baz=HASH(0x815c344)]
222        Observation A from [Baz=HASH(0x815c344)]
223
224       And since "Bar" is a child of "Foo" and each has one class-level
225       observer, running either:
226
227        my @observers = Baz->get_observers();
228        my @observers = $baz_a->get_observers();
229
230       would return a two-item list. The first item would be the "observer_b"
231       code reference, the second the "observer_a" code reference. Running:
232
233        my @observers = $baz_b->get_observers();
234
235       would return a three-item list, including the observer for that
236       specific object ("observer_c" coderef) as well as from its class (Baz)
237       and the parent (Foo) of its class.
238
239   Observers
240       There are three types of observers: classes, objects, and subroutines.
241       All three respond to events when "notify_observers()" is called from an
242       observable item. The differences among the three are are:
243
244       •   A class or object observer must implement a method "update()" which
245           is called when a state-change occurs. The name of the subroutine
246           observer is irrelevant.
247
248       •   A class or object observer must take at least two arguments: itself
249           and the observed item. The subroutine observer is obligated to take
250           only one argument, the observed item.
251
252           Both types of observers may also take an action name and a hashref
253           of parameters as optional arguments. Whether these are used depends
254           on the observed item.
255
256       •   Object observers can maintain state between responding to
257           observations.
258
259       Examples:
260
261       Subroutine observer:
262
263        sub respond {
264            my ( $item, $action, $params ) = @_;
265            return unless ( $action eq 'update' );
266            # ...
267        }
268        $observable->add_observer( \&respond );
269
270       Class observer:
271
272        package My::ObserverC;
273
274        sub update {
275            my ( $class, $item, $action, $params ) = @_;
276            return unless ( $action eq 'update' );
277            # ...
278        }
279
280       Object observer:
281
282        package My::ObserverO;
283
284        sub new {
285            my ( $class, $type ) = @_;
286            return bless ( { type => $type }, $class );
287        }
288
289        sub update {
290            my ( $self, $item, $action, $params ) = @_;
291            return unless ( $action eq $self->{type} );
292            # ...
293        }
294
295   Observable Objects and DESTROY
296       This class has a "DESTROY" method which must run when an instance of an
297       observable class goes out of scope in order to clean up the observers
298       added to that instance.
299
300       If there is no other destructor in the inheritance tree, this will end
301       up happening naturally and everything will be fine.
302
303       If it does not get called, then the list of observers will leak (which
304       also prevents the observers in it from being garbage-collected) and may
305       become associated with a different instance created later at the same
306       memory address as a previous instance.
307
308       This may happen if a class needs its own "DESTROY" method when it also
309       wants to inherit from Class::Observer (even indirectly!), because perl
310       only invokes the single nearest inherited "DESTROY".
311
312       The most straightforward (but maybe not best) way to ensure that the
313       destructor is called is to do something like this:
314
315         # in My::Class
316         sub DESTROY {
317             # ...
318             $self->Class::Observable::DESTROY;
319             # ...
320         }
321
322       A better way may be to to write all destructors in your class hierarchy
323       with the expectation that all of them will be called (which would
324       usually be preferred anyway) and then enforcing that expectation by
325       writing all of them as follows:
326
327         use mro;
328         sub DESTROY {
329             # ...
330             $self->maybe::next::method;
331             # ...
332         }
333
334       (Perl being Perl, of course, there are many other ways to go about
335       this.)
336

METHODS

338       notify_observers( [ $action, @params ] )
339
340       Called from the observed item, this method sends a message to all
341       observers that a state-change has occurred. The observed item can
342       optionally include additional information about the type of change that
343       has occurred and any additional parameters @params which get passed
344       along to each observer. The observed item should indicate in its API
345       what information will be passed along to the observers in $action and
346       @params.
347
348       Returns: Nothing
349
350       Example:
351
352        sub remove {
353            my ( $self ) = @_;
354            eval { $self->_remove_item_from_datastore };
355            if ( $@ ) {
356                $self->notify_observers( 'remove-fail', error_message => $@ );
357            }
358            else {
359                $self->notify_observers( 'remove' );
360            }
361        }
362
363       add_observer( @observers )
364
365       Adds the one or more observers (@observer) to the observed item. Each
366       observer can be a class name, object or subroutine -- see "Types of
367       Observers".
368
369       Returns: The number of observers now observing the item.
370
371       Example:
372
373        # Add a salary check (as a subroutine observer) for a particular
374        # person
375        my $person = Person->fetch( 3843857 );
376        $person->add_observer( \&salary_check );
377
378        # Add a salary check (as a class observer) for all people
379        Person->add_observer( 'Validate::Salary' );
380
381        # Add a salary check (as an object observer) for all people
382        my $salary_policy = Company::Policy::Salary->new( 'pretax' );
383        Person->add_observer( $salary_policy );
384
385       delete_observer( @observers )
386
387       Removes the one or more observers (@observer) from the observed item.
388       Each observer can be a class name, object or subroutine -- see "Types
389       of Observers".
390
391       Note that this only deletes each observer from the observed item
392       itself. It does not remove observer from any parent classes. Therefore,
393       if an observer is not registered directly with the observed item
394       nothing will be removed.
395
396       Returns: The number of observers now observing the item.
397
398       Examples:
399
400        # Remove a class observer from an object
401        $person->delete_observer( 'Lech::Ogler' );
402
403        # Remove an object observer from a class
404        Person->delete_observer( $salary_policy );
405
406       delete_all_observers()
407
408       Removes all observers from the observed item.
409
410       Note that this only deletes observers registered directly with the
411       observed item. It does not clear out observers from any parent classes.
412
413       WARNING: This method was renamed from "delete_observers". The
414       "delete_observers" call still works but is deprecated and will
415       eventually be removed.
416
417       Returns: The number of observers removed.
418
419       Example:
420
421        Person->delete_all_observers();
422
423       get_observers()
424
425       Returns all observers for an observed item, as well as the observers
426       for its class and parents as applicable. See "Observable Classes and
427       Objects" for more information.
428
429       Returns: list of observers.
430
431       Example:
432
433        my @observers = Person->get_observers;
434        foreach my $o ( @observers ) {
435            print "Observer is a: ";
436            print "Class"      unless ( ref $o );
437            print "Subroutine" if ( ref $o eq 'CODE' );
438            print "Object"     if ( ref $o and ref $o ne 'CODE' );
439            print "\n";
440        }
441
442       copy_observers( $copy_to_observable )
443
444       Copies all observers from one observed item to another. We get all
445       observers from the source, including the observers of parents. (Behind
446       the scenes we just use "get_observers()", so read that for what we
447       copy.)
448
449       We make no effort to ensure we don't copy an observer that's already
450       watching the object we're copying to. If this happens you will appear
451       to get duplicate observations. (But it shouldn't happen often, if
452       ever.)
453
454       Returns: number of observers copied
455
456       Example:
457
458        # Copy all observers of the 'Person' class to also observe the
459        # 'Address' class
460
461        Person->copy_observers( Address );
462
463        # Copy all observers of a $person to also observe a particular
464        # $address
465
466        $person->copy_observers( $address )
467
468       count_observers()
469
470       Counts the number of observers for an observed item, including ones
471       inherited from its class and/or parent classes. See "Observable Classes
472       and Objects" for more information.
473

RESOURCES

475       APIs for "java.util.Observable" and "java.util.Observer". (Docs below
476       are included with JDK 1.4 but have been consistent for some time.)
477
478       <http://java.sun.com/j2se/1.4/docs/api/java/util/Observable.html>
479
480       <http://java.sun.com/j2se/1.4/docs/api/java/util/Observer.html>
481
482       "Observer and Observable", Todd Sundsted,
483       <http://www.javaworld.com/javaworld/jw-10-1996/jw-10-howto_p.html>
484
485       "Java Tip 29: How to decouple the Observer/Observable object model",
486       Albert Lopez, <http://www.javaworld.com/javatips/jw-javatip29_p.html>
487

SEE ALSO

489       Class::ISA
490
491       Class::Trigger
492
493       Aspect
494

AUTHOR

496       Chris Winters <chris@cwinters.com>
497
499       This software is copyright (c) 2002-2004 Chris Winters.  This software
500       is copyright (c) 2021 by Aristotle Pagaltzis.
501
502       This is free software; you can redistribute it and/or modify it under
503       the same terms as the Perl 5 programming language system itself.
504
505
506
507perl v5.34.0                      2022-01-21              Class::Observable(3)
Impressum