The Observer Pattern

Sunday, September 25, 2011

The Observer pattern is one of my personal favorite design patterns. If you want the technical definition, Wikipedia has a good explanation. Because I'm fond of analogies, here is another way to look at Observer and Observable objects:

Imagine a waitress in a small restaurant. The waitress has certain responsibilities, such as taking orders from customers, bringing their food, etc. Because the waitress is working in a different area than other employees at the restaurant, she is likely to know of events that are unknown to other employees (such as the chefs who are in the kitchen). If something happens that is beyond the scope of her job duties, it is her job to notify the appropriate people so that they can act on this information. So let's say that there are 4 people working at this restaurant: the waitress, a chef, a janitor, and a manager. From the waitress's perspective, she is Observable, and she will notify her fellow coworkers (through Observers) of information they need to know. A few examples: a customer spills a drink; she might notify the janitor to clean it up. The customer asks to see a manager; she will notify the manager. The customer needs an extra side of mayo; she will notify the chef.

I don't want to give you the impression that observers are only notified when something goes wrong (although this is often the case). The waitress will also need to notify the chef every time an order is placed, and perhaps the manager whenever her shift is over.

The advantage to this pattern, is that the different objects aren't trying to do each other's jobs; they have clear responsibilities. Also, the observers can be added or removed at run time depending on the environment. For example, say that the waitress comes in and learns that the janitor is sick that day. She might need to notify someone else if there is a spill.

Using the example of above, here is some pseudo code showing how the restaurant employees relationship might be coded:

The Observer and Observable Interfaces

  1. <?php
  2. interface Observable
  3. {
  4. public function attach(Observer $observer);
  5. public function detach(Observer $observer);
  6. public function notify();
  7. }
  8. interface Observer
  9. {
  10. public function update(Observable $obs);
  11. }

These are the base Interfaces we will need to implement the Observer pattern. The methods will be defined in moment in the implementing classes. attach() is the method used to add an observer in the calling code, detach() is used to remove an observer, and notify() is used in the implementing class to call the update() method in each of the observer classes.

A Sample Observable "Employee" class

  1. <?php
  2. class Employee implements Observable
  3. {
  4. protected $observers = array();
  5. ##################################### OBSERVER METHODS
  6. public function attach(Observer $obs)
  7. {
  8. $key = get_class($obs);
  9. $this->observers[$key] = $obs;
  10. }
  11. public function detach(Observer $obs)
  12. {
  13. $key = get_class($obs);
  14. unset($this->observers[$key]);
  15. }
  16. public function notify()
  17. {
  18. foreach ($this->observers as $obs) {
  19. $obs->update($this);
  20. }
  21. }
  22. }

Keeping this class simple, we've only implemented the core methods for the Observable class.

A Sample Concrete "Waitress" Class

  1. <?php
  2. class Waitress extends Employee
  3. {
  4. public $noticedDrinkSpilled = false;
  5. public $hasAngryCustomer = false;
  6. public $currentFoodOrder = '';
  7. public function work()
  8. {
  9. echo 'Doing the following stuff: ';
  10. $this->notify();
  11. }
  12. }

Extending the Employee class provides us with all the observer methods, and we also define three properties and one method that are particular to the Waitress object

Three Sample Observer classes

  1. <?php
  2. class DrinkSpilledObserver implements Observer
  3. {
  4. public function update(Observable $employee)
  5. {
  6. if ($employee->noticedDrinkSpilled) {
  7. echo 'notifying janitor of spill<br />';
  8. $employee->noticedDrinkSpilled = false;
  9. }
  10. }
  11. }
  12. class AngryCustomerObserver implements Observer
  13. {
  14. public function update(Observable $employee)
  15. {
  16. if ($employee->hasAngryCustomer) {
  17. echo 'notifying manager about angry customer<br />';
  18. $employee->hasAngryCustomer = false;
  19. }
  20. }
  21. }
  22. class FoodOrderObserver implements Observer
  23. {
  24. public function update(Observable $employee)
  25. {
  26. if ($employee->currentFoodOrder != '') {
  27. echo 'passing food order of "'.$employee->currentFoodOrder.'" to chef<br />';
  28. $employee->currentFoodOrder = '';
  29. }
  30. }
  31. }

Tying it all Together: Sample Calling Code

  1. <?php
  2. $waitress = new Waitress;
  3. $waitress->attach(new DrinkSpilledObserver);
  4. $waitress->attach(new AngryCustomerObserver);
  5. $waitress->attach(new FoodOrderObserver);
  6. $waitress->currentFoodOrder = 'Burger & Fries';
  7. $waitress->work();
  8. $waitress->hasAngryCustomer = true;
  9. $waitress->work();
  10. $waitress->noticedDrinkSpilled = true;
  11. $waitress->work();

Output:

Doing the following stuff: passing food order of "Burger & Fries" to chef
Doing the following stuff: notifying manager about angry customer
Doing the following stuff: notifying janitor of spill

As you can see, the Waitress object and each of the 3 observers have tightly defined responsibilities. We can attach different observers in the calling code to provide different functionality depending on the environment and state we are in.

I hope this example helped you to understand how to use the Observer pattern, and gave you some ideas you can implement in your system.

Posted by Aaron Fisher at 3:12pm

0 Comments RSS

Login To Post A Comment

Don't have an account yet? Sign Up