Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a full Observer pattern in PHP

An Observer Design Pattern is the solution to loosely coupling objects so they can work together. In PHP you can easily implement this using just two classes.

Basically, you have a subject which is able to notify and update a list of observers of its state changes.

The problem I'm trying to solve is to know how to handler alerting the observers about different states of the object they are watching.

For example, lets say we have a file upload class to which we attach a logging class, websockets class, and a image resize class. Each of these classes that are watching want to know about different events in the upload process.

This file upload class might have three places where it needs to notify the classes listening that something has happend.

  • Error With Upload (alert logging class)
  • Upload success (alert websockets class)
  • Upload success and is image file (alert image resize class)

This is a very basic example, but how do you handle multiple events that different observers may need to know about? Calling notifyObservers() alone wouldn't be enough since each observer needs to know what it is being notified about.

One thought is that I could state with the call what type of event is being observed:

$this->notifyObservers('upload.error', this);

However, that would mean I would have to add custom switching to the observers themselves to know how to handle different events.

function observe($type, $object)
{
    if($type === 'upload.error') $this->dosomething();
    elseif($type === 'something.else') $this->otherthing();
    ...etc...
}

I find that very ugly as it starts to couple the observers back to the class they are observing.

Then again, if I just notify Observers without passing any information about what event just happens - they have to guess themselves what is going on which means more if() checks.

like image 426
Xeoncross Avatar asked Jun 16 '11 15:06

Xeoncross


1 Answers

The observers aren't actually coupled to the class they are observing. The connection between the observer's handler and the observed object is made using literal string values (e.g. `upload.error'), which means that:

  1. If you want to observe a specific object, you have to know from beforehand the names of the events it will publishing; this is the "coupling" that you don't like.
  2. On the other hand, if you are interested in a specific event only, you can observe any type of object for that event without having any knowledge about that object.

Item 2 above is a benefit that you care about, but what to do about item 1?

If you think about it, there needs to be some way to differentiate between callbacks to the same observer if they represent different events taking place. These "identifiers", no matter what form they take, need to be packaged either into the observed object or be a part of the observer library code.

In the first instance (inside observed object) you would probably need a way for observers to query "do you ever publish event X?" before starting to observe a target for that event. The target can answer this question just fine. This leaves a bitter taste of coupling, but if you want any object to observe any other, and you have no idea what you will be observing beforehand, I don't think you can do any better.

In the second approach, you would have a number of well-known events defined (as const inside a class?) in your library. Presumably such a list of events can be made because the library tackles a concrete application domain, and that domain offers obvious choices for the events. Then, classes both internal to your library (which would end up being observed) and external to it (the observers which plug into the framework) would use these identifiers to differentiate between events. Many callback-based APIs (such as Win32) use an approach practically identical to this.

like image 53
Jon Avatar answered Sep 22 '22 18:09

Jon