The Observer. I don't get it :(

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

The Observer. I don't get it :(

Post by Chris Corbyn »

I understand the code but I just don't see how it's useful. Maybe this is just a crap example. Could someone enlighten me on a scenario where this might be used? :)

I'm not even sure is this is a "basic" bare-bones observer or if they've added fluffy bits to show off.

This is from phptr.com:

Code: Select all

<?php
  interface Message
  {
   static function getType();
  };
  
  interface Observer
  {
   function notifyMsg(Message $msg);
  };
  
  class Subject
  {
   private $observers = array();
  
   function registerObserver(Observer $observer, $msgType)
   {
     $this->observers[$msgType][] = $observer;
   }
  
   private function notifyMsg(Message $msg)
   {
     @$observers = $this->observers[$msg->getType()];
     if(!$observers)
     {
      return;
     }

     foreach($observers as $observer)
     {
      $observer->notifyMsg($msg);
     }
   }
  
   function someMethod()
   {
     //fake some task
     sleep(1);

     //notify observers 
     $this->notifyMsg(new HelloMessage("Zeev"));
   }
  }

  
  class HelloMessage implements Message
  {
   private $name;
  
   function __construct($name)
   {
     $this->name = $name;
   }
  
   function getMsg()
   {
     return "Hello, $this->name!";
   }
  
   static function getType()
   {
     return "HELLO_TYPE";
   }
  }
  
  
  class MyObserver implements Observer
  {
   function notifyMsg(Message $msg)
   {
     if ($msg instanceof HelloMessage)
     {
      print $msg->getMsg();
     }  
   }
  }
  
  $subject = new Subject();
  $observer = new MyObserver();
  $subject->registerObserver($observer, 
   HelloMessage::getType());
  $subject->someMethod();
?>
Outputs:

Code: Select all

Hello, Zeev!
Obviously there's no reason why you can't have multiple observers here, all of which have some "type". The "type" is confusing me. Especially in this example. It looks as though any other loaded observers would execute the notifyMsg() method if they have the same type as "HelloMessage", but then the "Subject" object needs to know the name of at least one observer in this example for anything to happen (or am I missing something?). If that's right this doesn't seem overly flexible.

Can anybody enlighten me on these points?

a) Is this example the basics needed for the observer or have they added frilly bits?
b) Can anyone give a better example or describe a real situation where you'd use this?

:)

Other examples I found online started off with lengthy unit tests which I know nothing about at the moment :(
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: The Observer. I don't get it :(

Post by Chris Corbyn »

d11wtq wrote:The "type" is confusing me. Especially in this example. It looks as though any other loaded observers would execute the notifyMsg() method if they have the same type as "HelloMessage", but then the "Subject" object needs to know the name of at least one observer in this example for anything to happen
Ok I'm an idiot. I see it now. I misread where getType() was coming from. That helps a little :P I guess I could maybe see a place for this then :)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I have not used the pattern much, but it is very useful in certain circumstances. I find abstract discussion about it to be pretty worthless as well. A common example of a useful implementation is a spreadsheet where updates to a cell can change other cells, charts/graphs, etc.
(#10850)
User avatar
daedalus__
DevNet Resident
Posts: 1925
Joined: Thu Feb 09, 2006 4:52 pm

Post by daedalus__ »

What's an observer?
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

An observer is an object that listens to notifications send by an 'Observable'.

These notifications are send to the Observer through a welldefined 'interface', usually one function 'update(Observable source, ReasonObject reason)'.

I still haven't found a good reason to use this pattern in php code, but in java gui i happen to use it all the time. Eg: i have a splashscreen that observes the 'initalizing application process'... Everytime the loading process advances, it notifies the observers like: notify('now loading sample data into the dataase');... And then the splashscreen recieves this message and changes the text in it's progressbar...
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Daedalus- wrote:What's an observer?
It's an object that responds to events in another object.

In the example above we create an instance of "myObserver" which observes an instance of "Subject". Subject basically triggers the observer to run it's notifyMsg() method at certain points the author of "Subject" has defined; in this case that's when someMethod() runs.

Now, Subject calls all observers by basically going through a loop looking for all observers of the relevant type and executing their notifyMsg() methods. That's why we've got interfaces above.... all observers need a notifyMsg() method.

I guess I'll only really see where it's useful when one day I come across a need for it :)

See, it's pretty similar (apart from the - seems to me at least - lesser flexibility and absence of types) to:

Code: Select all

class Subject
{
    private $pluginCollection = array();
    
    public function loadPlugin($pluginObject, $id)
    {
        $this->pluginCollection[$id] = $pluginObject;
    }

    private function triggerEvent($event)
    {
        $handler = 'on'.$event; //e.g. onConnect
        foreach ($this->pluginCollection as $id => $plugin)
        {
            if (method_exists($plugin, $handler)) $this->pluginCollection[$id]->$handler();
        }
    }

    public function connect($host, $user, $pass)
    {
        mysql_connect($host, $user, $pass);
        $this->triggerEvent('Connect');
    }
}

class myPlugin
{
    public function onConnect()
    {
        echo 'Subject has connected!';
    }
}

$subject = new Subject;
$subject->loadPlugin(new myPlugin, 'myPlugin');
$subject->connect('localhost', 'username', 'password'); //Subject has connected!
I guess even that is sort of an observer even if it's not following that same pattern.
User avatar
daedalus__
DevNet Resident
Posts: 1925
Joined: Thu Feb 09, 2006 4:52 pm

Post by daedalus__ »

I've never seen interface before but couldn't an observer be useful for error reporting or is all of the built-in stuff sufficient?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Daedalus- wrote:I've never seen interface before but couldn't an observer be useful for error reporting or is all of the built-in stuff sufficient?
This was mentioned in another thread today :)

I guess you could have silent errors (boolean responses) but allow an observer to display errors. I'm actually thinking now though that it's a total overkill doing that if the only purpose is to display errors. But you could however make the observer not display the error, rather write it to a log file or a database. If you needed something like that I guess an observer might work. To be honest I'm likely as much in the dark as you on this one ;)

Let's see how it would look.

Code: Select all

class someClass
{
    private $observers = array();

    public function registerObserver($observer, $type)
    {
        if (!isset($this->observers[$type])) $this->observers[$type] = array();
        $this->observers[$type][] = $observer;
    }

    private function notify($object)
    {
        if (isset($this->observers[$object->getType()]))
        {
            foreach ($this->observers[$object->getType()] as $i => $obj)
            {
                $this->observers[$object->getType()][$i]->notify($object);
            }
        }
    }
    
    public function someMethodThatCanError()
    {
        if (/*something went wrong*/)
        {
            $this->notify(new errorMsg('Oops, something went wrong'));
            return false;
        }
        else //just do something
    }
}

class errorMsg
{
    private $errorString;
    
    public function __construct($error_string)
    {
        $this->errorString = $error_string;
    }

    public function getType()
    {
        return 'ERROR';
    }

    public function getErrorString()
    {
        return $this->errorString;
    }
}

class errorHandler
{
    public function notify($object)
    {
        if ($object instanceof errorMsg)
        {
            echo $object->getErrorString();
        }
    }
}

$someObject = new someClass;
$someObject->registerObserver(new errorHandler, errorMsg::getType());

$someObject->someMethodThatCanError();
Nah, seems a bit long winded.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

OK I guess a big difference between the oberserver & observable vs. plugin & pluggable is that plugins can modify the behaviour of the pluggable, whereas observers don't actually do anything to the observable right? If observers can tinker with the workings of the observable, have they crossed some fine line into plugin territory and out of observer territory? Am I just rambling on now? :P

I think I'll leave this pattern behind for now and move on until I see a need for it in PHP :)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

d11wtq wrote:OK I guess a big difference between the oberserver & observable vs. plugin & pluggable is that plugins can modify the behaviour of the pluggable, whereas observers don't actually do anything to the observable right? If observers can tinker with the workings of the observable, have they crossed some fine line into plugin territory and out of observer territory? Am I just rambling on now? :P
As ever with patterns the difference is often with intent. The intent of the Observer pattern is to notify some number of classes when an event occurs -- so it is a specific type of composite/component relationship. The Plugin is more akin to the Factory pattern in that that they allow multiple classes to be treated as one. With the Factory you are requesting objects, as opposed to the Plugin where you are talking to a interface. In either case you don't care what happens on the other side of the interface.
(#10850)
User avatar
sweatje
Forum Contributor
Posts: 277
Joined: Wed Jun 29, 2005 10:04 pm
Location: Iowa, USA

Post by sweatje »

It might also help to know that the Observer pattern is also know as Publish/Subscribe. It is useful in cases where you have a source of data, and you want to be flexible at runtime with one or more different means of responding to the data. In my book, I show an example of using an Observer in a PHP4 error handler, with different observers logging errors to a file, or to syslog or sending an email. (as a point of interest, I still periodically get emails from people randomly testing the code without changing from my email address :wink: )
JPlush76
Forum Regular
Posts: 819
Joined: Thu Aug 01, 2002 5:42 pm
Location: Los Angeles, CA
Contact:

Post by JPlush76 »

a good example of the observer pattern is in javascript

when a page loads you have an onload event that is fired in the browser. Sometimes you want to execute a function when the page loads... ok thats great but what if you want to execute 5 functions when the page loads? you could create a wrapper function to call that will then call each function but that is messy and not great to maintain. A better way is to use the observer pattern.

Here's a basic run through

while page is loading I register my 5 functions into an "observer" array

when the page is loaded the onload function loops through the array, calling each function to notify them the page has loaded.

Code: Select all

// SET UP OBSERVER PATTERN FOR LETTING JAVASCRIPT FUNCTIONS REGISTER THEMSELVES
onLoadListeners = new Array();	//container that will hold our observing functions
/**
* This function can be called globally in any page to add a function to be called for the onload event
*@param object A function name that should be called when the page is loaded IE: addOnLoad(myFunc);
*/
function addOnLoad(func) {
		var listener = new Array();
		listener['listener'] = func;
		onLoadListeners.push(listener);
}

// loop through all our listeners and alert them the page has loaded
window.onload=function()
{
	var len = onLoadListeners.length;
	for(var i=0; i<len; i++) {
		onLoadListeners[i].listener();
	}
}
JPlush76
Forum Regular
Posts: 819
Joined: Thu Aug 01, 2002 5:42 pm
Location: Los Angeles, CA
Contact:

Post by JPlush76 »

a good PHP example would be message board postings...

You can use the observer pattern when someone posts a message

When a user posts a message you usually want to:

Send out email notification of a new post

Update post counts

Update other counters

Update your RSS feed

So if each of those puppies were objects waiting for a message you can use this pattern to loop through the observers (the list above) and then execute their method with a message of what just occured.

Its one of the best patterns around
Post Reply