The Observer Pattern is designed to help cope with one to many relationships between objects, allowing changes in an object to update many associated objects. It provides a powerful mechanism to extend our applications, in terms of how they respond to events, without needing alter existing (and working) code. We'll take a simplified look at how the observer pattern might be used in a typical PHP forum application then suggest other uses for the observer pattern ======Under Observation====== (( {{design:observer.zip|Observer Sample Code}} )) The Observer pattern is defined by the Gang of Four in [[http://www.amazon.com/exec/obidos/ASIN/0201633612/pinkgoblin-20/|Design Patterns]] as //behavioural// pattern - that is one we can use to modify the behaviour of our applications. It's regarded as being so useful, that Sun bothered to implement it in the Java API (see [[http://java.sun.com/products/jdk/1.2/docs/api/java/util/Observable.html|Observable]] and [[http://java.sun.com/products/jdk/1.2/docs/api/java/util/Observer.html|Observer]]). The basic principle behind the observer pattern is if you have some object, such as a Post object for a forum system, you can have other objects, such as a Mailer object, act as an observer and respond to any changes in the Post object, such as emailing relevant forums users that a new post has been added to the thread they were subscribed to. Conceptually, the Observer pattern in something like a trigger in a database, which runs a stored procedure when a table row is modified for example (we won't go too far with this analogy though). Normally we might implement the mailing functionality in the Post object itself but what if later we want add further "events" to Post, when a new post is made, such as adding an entry to our forums RSS feed for example? And how many more things might we want to add in future? The observer pattern provides us the mechanism to add such functionality without needing to alter the Post object. The is easiest to see will some some example code. First we define two interface classes; an Observable class which will be inherited by the Post class and allows it to become the //subject// of observation and an Observer class which will be inherited by the classes used to observer Post. observers=array(); } //! An accessor /** * Calls the update() function using the reference to each * registered observer - used by children of Observable * @return void */ function notifyObservers () { $observers=count($this->observers); for ($i=0;$i<$observers;$i++) { $this->observers[$i]->update(); } } //! An accessor /** * Register the reference to an object object * @return void */ function addObserver (& $observer) { $this->observers[]=& $observer; } //! An accessor /** * Returns the current value of the state property * @return mixed */ function getState () { return $this->state; } //! An accessor /** * Assigns a value to state property * @param $state mixed variable to store * @return void */ function setState ($state) { $this->state=$state; } } ?> And subject=& $subject; // Register this object so subject can notify it $subject->addObserver($this); } //! An accessor /** * Abstract function implemented by children to repond to * to changes in Observable subject * @return void */ function update() { trigger_error ('Update not implemented'); } } ?> Notice in the construction of Observer, it is passed a reference to an instance of Observable then calls the addObserver() method in Observable to register itself as a "watching" object. Note we've taken only one of a number possible approaches to "relating" the subject observable classes and watching observer classes (more on this in a moment). Now we have those, we can extend the Observable class with Post (note we're only going to simulate these classes here - writing an entire forum application for this demonstration is a little more than time allows...); addPost(): Dummy post added
' ); // Set the state $this->setState('Post added'); // Call the parent function to notify all observers $this->notifyObservers(); } } ?>
Notice how the addPost() method calls it's parent notifyObservers() method. This means that when a post is added, all observers will be notified of the change and can react appropriately. Apart from that, we've said nothing about what will be observing the Post object when instantiated. Post knows nothing (and doesn't care) about the objects observing it. Now we define a class Mailer which will inherit from the from Observer class; subject->getState() == 'Post added' ) { $this->sendMail(); } } //! An accessor /** * Simulates the sending of email to all users subscribed to the * thread * @return void */ function sendMail () { echo ( '$mailer->sendMail(): Sending mail to all users subscribed ' .'to this thread
' ); // mail () etc... } } ?>
We've defined a method update() here which all children of Observer must have (as it will be used by the Observable class when performing the notifyObservers() method). The update() method checks the state of the subject observable class and decides what it should do. Now, to demonstrate the point, we'll define a second observer called Feed, which simulates updating the XML file used to publish an RSS feed for our site. subject->getState() == 'Post added' ) { $this->updateFeed(); } } //! An accessor /** * Simulates the updating of the RSS feed * @return void */ function updateFeed () { echo ( '$feed->updateFeed(): Updating RSS feed
' ); // Edit XML file... } } ?>
In UML terms this looks something like; {{design:Observer.png?200x60}} Note that the above diagram isn't strictly correct as the Observable and Observer classes are related by association - the diagram is intended to show how the Observable pattern notifies the the Observers. Now lets see how these classes might be put into action; Observer Pattern Demo addPost(); ?> Notice how we //only// call the addPost() method from the instance of Post. The HTML output looks something like this; $post->addPost(): Dummy post added $mailer->sendMail(): Sending mail to all users subscribed to this thread $feed->updateFeed(): Updating RSS feed The instances Mailer and Feed automatically respond to the the event of adding a post! When they were instantiated, Mailer and Feed "registered" themselves with the instance of Post, basically saying "let me know of any changes". When the addPost() method was called, Post simply notified Mailer and Feed that a change had occurred and left it to them to decided whether they needed to react. What's more, depending on how we design the observer classes, we could reuse them with other observable objects. The observer pattern can be readily applied to many common PHP problems. Some examples; - Building a user registration system, where we might add observers for sending registration emails, checking the user's email MX record as valid and so on (this example is used in [[http://www.amazon.com/exec/obidos/ASIN/0201715945/pinkgoblin-20/|Design Patterns Explains]] using Java as a the reference language). - "Watching" updates made to a Log file, as in [[http://pear.php.net/package-info.php?pacid=8|PEAR::Log]] which is discussed in [[http://www.horde.org/papers/kongress2002-design_patterns/12_observer.xml.html|this presentation]] - the approach taken in PEAR for constructing an observer pattern is somewhat different to the one we've used here, using [[design:singleton_pattern|Singleton]] and Factory method patterns. - As a tool for performing multiple INSERTs / UPDATEs to a database (which can come in handy for a database like MySQL, where triggers are not supported). Note that if we want to avoid having to modify the subject class (Post in our example) or the observer classes, to inherit from the base classes, we can use the [[design:adapters_and_proxy_patterns|Adapter pattern]]. ======Further Reading====== [[http://www.javaworld.com/javaworld/javaqa/2001-05/04-qa-0525-observer.html|JavaWorld Q&A on Observer Patterns]]