Main Page | See live article | Alphabetical index

Event listener

In computer programming, event listener is one of design patternss that is to distribute data to objects that volunteer for it, but you don't want to maintain a central list of where it should go, especially if it changes over the life of a running program. You set up a registration method, keep a list of interested objects that register themselves, and each time something arrives you, you send it to everyone who has registered.

When: Information is coming in that needs to be distributed to an unknown or changing list of objects.

Symptoms: When information arrives, you loop through an array and call a method in every object listed, or worse, looking through a large if/elsif chain looking for a method or object to call. You're often adding things, which requires changes to the array of if/elsif chain as well as creating the new object or procedure.

Also Known As: Observer.

Decide on a list of different kinds of events that are happening, and create a channel for each of them. When something subscribes to that channel, it gets notification of everything that comes in on it. In this case, there is one source and potentially many recipients. If the recipients also distribute information, there is no reason that they cannot also implement channels, though this might require the use of threads.

 package EventChannel;
 
 sub new {
   my $type = shift;
   my $me = { };
   bless $me, $type;
 }
 
 sub addEventListener {
   my $me = shift;
   my $eventListener = shift; $eventListener->isa('EventListener') or die;
   $me->{$eventListener} = $eventListener;
   return 1;
 }
 
 sub removeEventListener {
   my $me = shift;
   my $eventListener = shift; $eventListener->isa('EventListener') or die;
   delete $me->{$eventListener};
   return 1;
 }
 
 sub broadcastEvent {
   my $me = shift;
   my $event = shift;
   foreach my $i (values %$me) {
     eval { $i->receiveEvent($event); };
     warn $@ if($@);
   }
 }
 
 package EventListener;
 
 sub receiveEvent {
   warn "EventListener::receiveEvent not overriden to do anything useful";
 }
 
 package IOServer;
 use EventChannel;
 
 sub new {
   my $type = shift;
   my $filehandle = shift;
   my $me = {fileHandle=>$filehandle};
 }
 
 sub start {
   my $me = shift;
   my $filehandle = $me->{'fileHandle'};
   while(<$filehandle>) {
     $me->broadcastEvent($_);
   }
 }
 
 # WonderWedge
 
 package WonderWedge;
 use EventListener;
 @ISA = ('EventListener');
 
 sub new {
   my $type = shift;
   my $me = { };
   bless $me, $type;
 }
 
 sub receiveEvent {
   my $me = shift;
   print @_;
 }
 
 # main
 
 package main;
 use WonderWedge;
 use IOServer; 
 
 my $stdinserver = new IOServer \\*STDIN;
 $stdinserver->addEventListener(new WonderWedge);
 $stdinserver->start();
 
An event itself could, and often should, be represented as an object, too. In this example, we'll leave it as a scalar.

I have a Gnutella servent that allows user defined code to attach to it as daemons, in addition to a few built in ones. If I broadcast every message to every daemon object, I would be spending more time delivering messages than processing them, since most daemons aren't interested in most messages. Instead, interested daemons can register interest in connection notices, pong packs, search requests, and many other things that come in over the network. In fact, connections are presented as daemons themselves, and they listen for requests to relay or broadcast information from other daemons in the system. Since connections and vanish all of the time, it would be impossible to know in advance where to send packets. The solution is to have interested parties request them.

An advanced version of this is a ConstraintSystem.

External Links

The article is originally from the Perl Design Patterns Book