Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
Class::Observable(3)  User Contributed Perl Documentation Class::Observable(3)

NAME
       Class::Observable - Allow other classes and objects to respond to
       events in yours

SYNOPSIS
	 # Define an observable	class

	 package My::Object;

	 use base qw( Class::Observable	);

	 # Tell	all classes/objects observing this object that a state-change
	 # has occurred

	 sub create {
	    my ( $self ) = @_;
	    eval { $self->_perform_create() };
	    if ( $@ ) {
		My::Exception->throw( "Error saving: $@" );
	    }
	    $self->notify_observers();
	 }

	 # Same	thing, except make the type of change explicit and pass
	 # arguments.

	 sub edit {
	    my ( $self ) = @_;
	    my %old_values = $self->extract_values;
	    eval { $self->_perform_edit() };
	    if ( $@ ) {
		My::Exception->throw( "Error saving: $@" );
	    }
	    $self->notify_observers( 'edit', old_values	=> \%old_values	);
	 }

	 # Define an observer

	 package My::Observer;

	 sub update {
	    my ( $class, $object, $action ) = @_;
	    unless ( $action ) {
		warn "Cannot operation on [", $object->id, "] without action";
		return;
	    }
	    $class->_on_save( $object )	  if ( $action eq 'save' );
	    $class->_on_update(	$object	) if ( $action eq 'update' );
	 }

	 # Register the	observer class with all	instances of the observable
	 # class

	 My::Object->add_observer( 'My::Observer' );

	 # Register the	observer class with a single instance of the
	 # observable class

	 my $object = My::Object->new( 'foo' );
	 $object->add_observer(	'My::Observer' );

	 # Register an observer	object the same	way

	 my $observer =	My::Observer->new( 'bar' );
	 My::Object->add_observer( $observer );
	 my $object = My::Object->new( 'foo' );
	 $object->add_observer(	$observer );

	 # Register an observer	using a	subroutine

	 sub catch_observation { ... }

	 My::Object->add_observer( \&catch_observation );
	 my $object = My::Object->new( 'foo' );
	 $object->add_observer(	\&catch_observation );

	 # Define the observable class as a parent and allow the observers to
	 # be used by the child

	 package My::Parent;

	 use strict;
	 use base qw( Class::Observable	);

	 sub prepare_for_bed {
	     my	( $self	) = @_;
	     $self->notify_observers( 'prepare_for_bed'	);
	 }

	 sub brush_teeth {
	     my	( $self	) = @_;
	     $self->_brush_teeth( time => 45 );
	     $self->_floss_teeth( time => 30 );
	     $self->_gargle( time => 30	);
	 }

	 sub wash_face { ... }

	 package My::Child;

	 use strict;
	 use base qw( My::Parent );

	 sub brush_teeth {
	     my	( $self	) = @_;
	     $self->_wet_toothbrush();
	 }

	 sub wash_face { return	}

	 # Create a class-based	observer

	 package My::ParentRules;

	 sub update {
	     my	( $item, $action ) = @_;
	     if	( $action eq 'prepare_for_bed' ) {
		 $item->brush_teeth;
		 $item->wash_face;
	     }
	 }

	 My::Parent->add_observer( __PACKAGE__ );

	 $parent->prepare_for_bed # brush, floss, gargle, and wash face
	 $child->prepare_for_bed  # pretend to brush, pretend to wash face

DESCRIPTION
       If you have ever	used Java, you may have	run across the
       "java.util.Observable" class and	the "java.util.Observer" interface.
       With them you can decouple an object from the one or more objects that
       wish to be notified whenever particular events occur.

       These events occur based	on a contract with the observed	item. They may
       occur at	the beginning, in the middle or	end of a method. In addition,
       the object knows	that it	is being observed. It just does	not know how
       many or what types of objects are doing the observing. It can therefore
       control when the	messages get sent to the obsevers.

       The behavior of the observers is	up to you. However, be aware that we
       do not do any error handling from calls to the observers. If an
       observer	throws a "die",	it will	bubble up to the observed item and
       require handling	there. So be careful.

       Throughout this documentation we	refer to an 'observed item' or
       'observable item'. This ambiguity refers	to the fact that both a	class
       and an object can be observed. The behavior when	notifying observers is
       identical. The only difference comes in which observers are notified.
       (See "Observable	Classes	and Objects" for more information.)

   Observable Classes and Objects
       The observable item does	not need to implement any extra	methods	or
       variables. Whenever it wants to let observers know about	a state-change
       or occurrence in	the object, it just needs to call
       "notify_observers()".

       As noted	above, whether the observed item is a class or object does not
       matter -- the behavior is the same. The difference comes	in determining
       which observers are to be notified:

       o   If the observed item	is a class, all	objects	instantiated from that
	   class will use these	observers. In addition,	all subclasses and
	   objects instantiated	from the subclasses will use these observers.

       o   If the observed item	is an object, only that	particular object will
	   use its observers. Once it falls out	of scope then the observers
	   will	no longer be available.	(See "Observable Objects and DESTROY"
	   below.)

       Whichever you chose, your documentation should make clear which type of
       observed	item observers can expect.

       So given	the following example:

	BEGIN {
	    package Foo;
	    use	base qw( Class::Observable );
	    sub	new { return bless( {},	$_[0] )	}
	    sub	yodel {	$_[0]->notify_observers	}

	    package Baz;
	    use	base qw( Foo );
	    sub	yell { $_[0]->notify_observers }
	}

	sub observer_a { print "Observation A from [$_[0]]\n" }
	sub observer_b { print "Observation B from [$_[0]]\n" }
	sub observer_c { print "Observation C from [$_[0]]\n" }

	Foo->add_observer( \&observer_a	);
	Baz->add_observer( \&observer_b	);

	my $foo	= Foo->new;
	print "Yodeling...\n";
	$foo->yodel;

	my $baz_a = Baz->new;
	print "Yelling A...\n";
	$baz_a->yell;

	my $baz_b = Baz->new;
	$baz_b->add_observer( \&observer_c );
	print "Yelling B...\n";
	$baz_b->yell;

       You would see something like

	Yodeling...
	Observation A from [Foo=HASH(0x80f7acc)]
	Yelling	A...
	Observation B from [Baz=HASH(0x815c2b4)]
	Observation A from [Baz=HASH(0x815c2b4)]
	Yelling	B...
	Observation C from [Baz=HASH(0x815c344)]
	Observation B from [Baz=HASH(0x815c344)]
	Observation A from [Baz=HASH(0x815c344)]

       And since "Bar" is a child of "Foo" and each has	one class-level
       observer, running either:

	my @observers =	Baz->get_observers();
	my @observers =	$baz_a->get_observers();

       would return a two-item list. The first item would be the "observer_b"
       code reference, the second the "observer_a" code	reference. Running:

	my @observers =	$baz_b->get_observers();

       would return a three-item list, including the observer for that
       specific	object ("observer_c" coderef) as well as from its class	(Baz)
       and the parent (Foo) of its class.

   Observers
       There are three types of	observers: classes, objects, and subroutines.
       All three respond to events when	"notify_observers()" is	called from an
       observable item.	The differences	among the three	are are:

       o   A class or object observer must implement a method "update()" which
	   is called when a state-change occurs. The name of the subroutine
	   observer is irrelevant.

       o   A class or object observer must take	at least two arguments:	itself
	   and the observed item. The subroutine observer is obligated to take
	   only	one argument, the observed item.

	   Both	types of observers may also take an action name	and a hashref
	   of parameters as optional arguments.	Whether	these are used depends
	   on the observed item.

       o   Object observers can	maintain state between responding to
	   observations.

       Examples:

       Subroutine observer:

	sub respond {
	    my ( $item,	$action, $params ) = @_;
	    return unless ( $action eq 'update'	);
	    # ...
	}
	$observable->add_observer( \&respond );

       Class observer:

	package	My::ObserverC;

	sub update {
	    my ( $class, $item,	$action, $params ) = @_;
	    return unless ( $action eq 'update'	);
	    # ...
	}

       Object observer:

	package	My::ObserverO;

	sub new	{
	    my ( $class, $type ) = @_;
	    return bless ( { type => $type }, $class );
	}

	sub update {
	    my ( $self,	$item, $action,	$params	) = @_;
	    return unless ( $action eq $self->{type} );
	    # ...
	}

   Observable Objects and DESTROY
       Previous	versions of this module	had a problem with maintaining
       references to observable	objects/coderefs. As a result they'd never be
       destroyed. As of	1.04 we're using weak references with "weaken" in
       Scalar::Util so this shouldn't be a problem any longer.

METHODS
   Observed Item Methods
       notify_observers( [ $action, @params ] )

       Called from the observed	item, this method sends	a message to all
       observers that a	state-change has occurred. The observed	item can
       optionally include additional information about the type	of change that
       has occurred and	any additional parameters @params which	get passed
       along to	each observer. The observed item should	indicate in its	API
       what information	will be	passed along to	the observers in $action and
       @params.

       Returns:	Nothing

       Example:

	sub remove {
	    my ( $self ) = @_;
	    eval { $self->_remove_item_from_datastore };
	    if ( $@ ) {
		$self->notify_observers( 'remove-fail',	error_message => $@ );
	    }
	    else {
		$self->notify_observers( 'remove' );
	    }
	}

       add_observer( @observers	)

       Adds the	one or more observers (@observer) to the observed item.	Each
       observer	can be a class name, object or subroutine -- see "Types	of
       Observers".

       Returns:	The number of observers	now observing the item.

       Example:

	# Add a	salary check (as a subroutine observer)	for a particular
	# person
	my $person = Person->fetch( 3843857 );
	$person->add_observer( \&salary_check );

	# Add a	salary check (as a class observer) for all people
	Person->add_observer( 'Validate::Salary' );

	# Add a	salary check (as an object observer) for all people
	my $salary_policy = Company::Policy::Salary->new( 'pretax' );
	Person->add_observer( $salary_policy );

       delete_observer(	@observers )

       Removes the one or more observers (@observer) from the observed item.
       Each observer can be a class name, object or subroutine -- see "Types
       of Observers".

       Note that this only deletes each	observer from the observed item
       itself. It does not remove observer from	any parent classes. Therefore,
       if an observer is not registered	directly with the observed item
       nothing will be removed.

       Returns:	The number of observers	now observing the item.

       Examples:

	# Remove a class observer from an object
	$person->delete_observer( 'Lech::Ogler'	);

	# Remove an object observer from a class
	Person->delete_observer( $salary_policy	);

       delete_all_observers()

       Removes all observers from the observed item.

       Note that this only deletes observers registered	directly with the
       observed	item. It does not clear	out observers from any parent classes.

       WARNING:	This method was	renamed	from "delete_observers". The
       "delete_observers" call still works but is deprecated and will
       eventually be removed.

       Returns:	The number of observers	removed.

       Example:

	Person->delete_all_observers();

       get_observers()

       Returns all observers for an observed item, as well as the observers
       for its class and parents as applicable.	See "Observable	Classes	and
       Objects"	for more information.

       Returns:	list of	observers.

       Example:

	my @observers =	Person->get_observers;
	foreach	my $o (	@observers ) {
	    print "Observer is a: ";
	    print "Class"      unless (	ref $o );
	    print "Subroutine" if ( ref	$o eq 'CODE' );
	    print "Object"     if ( ref	$o and ref $o ne 'CODE'	);
	    print "\n";
	}

       copy_observers( $copy_to_observable )

       Copies all observers from one observed item to another. We get all
       observers from the source, including the	observers of parents. (Behind
       the scenes we just use "get_observers()", so read that for what we
       copy.)

       We make no effort to ensure we don't copy an observer that's already
       watching	the object we're copying to. If	this happens you will appear
       to get duplicate	observations. (But it shouldn't	happen often, if
       ever.)

       Returns:	number of observers copied

       Example:

	# Copy all observers of	the 'Person' class to also observe the
	# 'Address' class

	Person->copy_observers(	Address	);

	# Copy all observers of	a $person to also observe a particular
	# $address

	$person->copy_observers( $address )

       count_observers()

       Counts the number of observers for an observed item, including ones
       inherited from its class	and/or parent classes. See "Observable Classes
       and Objects" for	more information.

   Debugging Methods
       Note that the debugging messages	will try to get	information about the
       observed	item if	called from an object. If you have an "id()" method in
       the object its value will be used in the	message, otherwise it will be
       described as "an	instance of class Foo".

       SET_DEBUG( $bool	)

       Turn debugging on or off. If set	the built-in implementation of
       "observer_log()"	will issue a warn at appropriate times during the
       process.

       observer_log( @message )

       Issues a	"warn" if "SET_DEBUG" hsa been called with a true value. This
       gets called multiple times during the registration and notification
       process.

       To catch	the "warn" calls just override this method.

       observer_error( @message	)

       Issues a	"die" if we catch an exception when notifying observers. To
       catch the "die" and do something	else with it just override this
       method.

RESOURCES
       APIs for	"java.util.Observable" and "java.util.Observer". (Docs below
       are included with JDK 1.4 but have been consistent for some time.)

       <http://java.sun.com/j2se/1.4/docs/api/java/util/Observable.html>

       <http://java.sun.com/j2se/1.4/docs/api/java/util/Observer.html>

       "Observer and Observable", Todd Sundsted,
       <http://www.javaworld.com/javaworld/jw-10-1996/jw-10-howto_p.html>

       "Java Tip 29: How to decouple the Observer/Observable object model",
       Albert Lopez, <http://www.javaworld.com/javatips/jw-javatip29_p.html>

SEE ALSO
       Class::ISA

       Class::Trigger

       Aspect

COPYRIGHT
       Copyright (c) 2002-2004 Chris Winters. All rights reserved.

       This library is free software; you can redistribute it and/or modify it
       under the same terms as Perl itself.

AUTHOR
       Chris Winters <chris@cwinters.com>

perl v5.24.1			  2004-10-16		  Class::Observable(3)

NAME | SYNOPSIS | DESCRIPTION | METHODS | RESOURCES | SEE ALSO | COPYRIGHT | AUTHOR

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=Class::Observable&sektion=3&manpath=FreeBSD+12.0-RELEASE+and+Ports>

home | help