Observer is a pattern where an object called the subject maintains a list of dependents (observers) and notifies them of any state changes. I implemented this recently to log changes to an Account object, and to notify the account holder if their account had expired – here’s the basics of the code, hopefully it’ll help someone else figure out how the pattern works
<?php
interface Observer
{
public function update(Observable $subject);
}
interface Observable
{
public function attachObserver(Observer $dependent);
public function detachObserver(Observer $dependent);
public function notify();
}
class Account implements Observable
{
public $status;
private $_observers = array();
public function __construct()
{
$this->attachObserver(new Logger());
$this->attachObserver(new Mailer());
}
public function attachObserver(Observer $object)
{
$this->_observers[] = $object;
}
public function detachObserver(Observer $object)
{
foreach ($this->_observers as $index => $observer) {
if ($object == $observer) {
unset($this->_observers[$index]);
}
}
}
public function notify()
{
foreach ($this->_observers as $observer) {
$observer->update($this);
}
}
public function save()
{
$this->notify($this);
$this->notify();
}
}
class Logger implements Observer
{
public function update(Observable $subject)
{
//Update status in log table
echo "Updating status in log table.\n";
}
}
class Mailer implements Observer
{
public function update(Observable $subject)
{
switch (get_class($subject)) {
case "Account":
if ($subject->status == "Expired") {
//send email: "account expired"
echo "Sending account expired email.\n";
}
}
}
}
$account = new Account();
$account->status = "Expired";
$account->save();
The interfaces aren’t strictly necessary, but they help make the whole thing clearer (I hope).
$this->notify($this);
October 12, 2010 at 3:11 pm
nice. I’ve never had to implement it, so never looked at it, but now that I’ve seen it written, I can imagine some uses in my own stuff (for example, watching a file’s status, and calling various plugins if it changes)
only thing I’d say is to remove the $this parameter from “$this->notify($this);” – that method doesn’t have any parameters in its header, and surely the $this in the method would always be from the same instance anyway?
October 12, 2010 at 3:21 pm
Oops! You’re right of course, thanks Kae
October 14, 2010 at 2:23 pm
Is there any reason why you chose not to implement SplObserver and SplSubject?
October 15, 2010 at 12:28 pm
Yes – I didn’t know about them until now
Will fill those gaps in my knowledge asap
October 16, 2010 at 8:59 am
Don’t worry – from a design point of view, it doesn’t make any sense to implement patterns in a library like SPL. It would force you to use a particular style of Observer (pull), to not refer to your classes in type hinting, and to implement all methods even if you only need to attach Observers and not to detach them, nor to expose notify().
October 14, 2010 at 2:40 pm
I always add a method to the subject (Observable) with wich I can get get the current status of it, this because sometimes the subject release several events to the observers, and they need to know if are interested or not in the notification.
October 14, 2010 at 4:23 pm
Thanks for your explanation. Like Kae, I’ve never had the need to use this pattern, but it’s certainly more clear now
Regards,
George
October 14, 2010 at 6:42 pm
Hi
Thanks for a very interesting post. Just wanted to point in the direction of SplObserver and SplSubject classes that performs similar to your Observer and Observable classes. Had a little fun rewriting your cool little example using those. See http://pastebin.com/ZPHPtA0d. Also notice the use of the ultra usefull SplObjectStorage class.
Cheers!
October 14, 2010 at 6:46 pm
Notice that I refactored the subject pattern specific code to the class RuneSubject to make an example.
October 14, 2010 at 7:01 pm
Thumbs up from me too. I already have this in a code and a friend programmer did not understood my code… but he understood yours. (Of course I didn’t had time to extract from the project a similar simple example.)
One small note. In “function detachObserver(Observer $object)”… it would probably be unlikely to have “the same object” registered twice so after “unset…” I would go for a “return;”… what do you think ?
October 15, 2010 at 10:56 am
Great and simple example of the Observer pattern. Thanks for this article
October 18, 2010 at 6:46 am
Even though i have seen this pattern dozens of times, especially in java, i have never coded it in php
i think you provided a nice example
The thing i dont like about this pattern is the generic naming … i always forget which method does what (notify / update) as for me they are meaningless. Maybe GOF could come up with something like notifyListeners or handleEvent or something even better. Especially in PHP that does not play nicely as there is no overloading like in Java or C#. You cant have any class implement subject interface because they may have update methods already. If your logger had an update method then you would be in trouble :/
Its a nice example for PHP though
cheers !