Hack67.Observe Your Objects


Hack 67. Observe Your Objects

Use the Observer pattern to loosely couple your objects.

Loose coupling is critical to any large-scale project, but few people actually understand what the term really means. Have you ever made a small change in a project, and it seems that as a result, almost everything else has to change as well? This occurs all too often because of tight coupling among the modules in the program. Each module relies on the exact state or function of several other modules. When one fails, they all fail. When one changes, they all must change.

The Observer pattern loosens up the bonds among objects by providing a simpler intra-object contract. An object allows itself to be observed by providing a mechanism where objects can register with it. When the observed object changes, it notifies the observing objects through a notification object. The observed object does not care how or why it is being observed, nor does it even know what types of objects are observing it. Further, the observers usually don't care why or how the object is changing; all they are looking for is a change.

A classic example is the code in a dialog observing the state of a checkbox. The checkbox doesn't care if it's being observed by one object or a thousand objects. It simply sends out a message when its state changes. In the same way, the dialog doesn't care how the checkbox is implemented; it only cares about the box's state and about being notified when that state changes.

In this hack, I'll demonstrate the Observer pattern by setting up an observable customer list. This object represents a database table of customers. The CustomerList object will send out notifications when new customers are added. The object uses a SubscriptionList object to implement its observability. The listeners object is an instance of SubscriptionList that other objects can use to register themselves with CustomerList. Listeners use the add( ) method to add themselves to the list, and CustomerList uses the invoke() method to send out a message to the listeners. It doesn't matter if there are no listeners, or if there are thousands of listeners. The beauty here is that listening objects have no direct interaction with or dependence on CustomerList; listeners are insulated from the customers by the SubscriptionList class.

In this example, there will be one listener: a Log object that outputs any messages sent from CustomerList to the console. The relationship between the objects is shown in Figure 7-1.

Figure 7-1. CustomerList and its SubscriptionList with attached Log


7.2.1. The Code

Save the code in Example 7-1 as observer.php.

Example 7-1. The Observer pattern example
 <?php class Log {   public function message( $sender, $messageType, $data )   { print $messageType." - ".$data."\n";   } } class SubscriptionList {   var $list = array();   public function add( $obj, $method ) { $this->list []= array( $obj, $method ); } public function invoke() {   $args = func_get_args();   foreach( $this->list as $l ) { call_user_func_array( $l, $args ); } }   }   class CustomerList   { public $listeners; public function CustomerList() {   $this->listeners = new SubscriptionList(); } public function addUser( $user ) {   $this->listeners->invoke( $this, "add", "$user" ); }   }   $l = new Log();   $cl = new CustomerList();   $cl->listeners->add( $l, 'message' );   $cl->addUser( "starbuck" );   ?> 

7.2.2. Running the Hack

You run this code on the command line like this:

 % php observer.php add - starbuck 

The code first creates a log and a customer list. Then the log is subscribed to the customer list using the add() method. The final step is to add a user to the customer list. The addition of the customer fires off a message to the listenersin this case, the logthat puts out the message about the addition of the customer.

It would be easy to extend this code either to do some customer provisioning based on the addition of a customer or to send out a new user emailboth without changing the code in CustomerList. This is loose coupling, and it's why the Observer pattern is so important.

There are innumerable uses for the Observer pattern in software development. Windowing systems use Observer patterns and call them events. Companies like Tibco run their entire business model via the Observer pattern, connecting large business systems like Human Resources and Payroll. Database systems use an Observer pattern and call code that listens to event triggers. These triggers are activated when certain types of records are changed in the database. An Observer-patterned approach is also handy whenever you think a state change is relevant but don't yet understand to whom it will be relevant; you can code the listeners later and not tie them to the object that will be observed.

One potential "gotcha" with the Observer pattern is the infinite loop. This can happen when items that observe a system can also alter that system. For example, a drop-down combo alters a value and tells the data structure about it. That data structure then notifies the drop-down combo that the value has changed, whereupon the drop-down combo changes its value to match, only to send out another notification to the data structure, and so on. The easiest way to solve this problem is to code the drop-down combo so that recursion is prevented. It should simply ignore a message from the data structure if it's currently in the middle of notifying the data structure about a new value.

7.2.3. See Also

  • "Turn Any Object into an Array" [Hack #53]

  • "Create a Message Queue" [Hack #50]



PHP Hacks
PHP Hacks: Tips & Tools For Creating Dynamic Websites
ISBN: 0596101392
EAN: 2147483647
Year: 2006
Pages: 163

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net