The Observer pattern and Action Queues

Posted:

There is a well-know design pattern called publish-subscribe or the Observer model. The problem this model attempts to solve is one in which a object requires one or more parties to act on it when it changes to a particular state. A concrete example of this event handlers in GUIs including the DOM. Actions made be associated with button presses.

The Observer model seperates the subject from the parties (observers) that act on it. Observers register the interest with the subject. When the subject changes to the desired state, it notifies each of the registered observers.

In PHP, a subject class might be modeled like this:

class Subject {
  private $Q = array();
  function Attach($O) {
     array_push($this->Q, $O);
  }

  function Detach($O) { 
    for($i=0; $i < count($Q); $i++) {
      if ($Q[$i] == $O) {
        array_splice($this->Q, $i, 1);
    break;
      }      
    }
  } 

  function Notify() {
    foreach($this->Q as $O) {
      $O->Update($this);
    }
  }  
}

The Attach() and Detach() methods are the API by which Observers register or unregister their interest in a Subject object. When the subject changes into an interesting state, its Notify() method is called. This method in turn calls the Observer Update() method with a reference to the current Subject object. An Observer class might look like this:

class Observer {
  function Update($S) {
     // Do something interesting
  }    
}

As you can see, an Observer need only implement one well-known method, Update(). This arrangement nicely decouples the Subject from the Observers.

There may be times when a less formal, more functional mechanism is desirable. What if you want just want certain actions to happen on an object when an interesting state obtains? You might use what I call an Action queue to do this. An Action Queue is simply an array of function references that are called by an object at an interesting time. Here’s what an Action queue might look like:

class MyClass {
   private $Q = array();

   function Attach($name, $func) {
      $this->Q[$name] = $func; 
   }

   function Detach($name) {
      unset($this->Q[$name]);
   }

   function Notify() { 
     foreach ($this->Q as $n=>$func) {
        $func($this);
     }
   }
} 

As you can see, there is no need for an Observer class. Bits of functionality created with create_function() can be attached ad hoc to this class, as the following snipet shows:

  $appender = create_function('$obj', 'return "Got => ".\$obj');
  $MyClassObj->Attach("append", $appender);

Because these code bits are anonymous, an arbitary name is required during the Attach phase in the event that you might want to remove the behavior later.