Login

An Introduction to the Observer Pattern in PHP

If you have reached the point in your programming life where you are using design patterns, you will want to read this article. The first of a three-part series, it covers the Observer pattern, which can be just the thing for situations where objects need to send information to a centralized mechanism.

Introduction

Design patterns can be hard to grasp sometimes. They’re a fundamental part of advanced software development, and possibly this fact makes them even more intimidating to those developers starting to taste their real power. However, many things in software programming aren’t as difficult as they seem at first glance, and that applies to learning design patterns as well.

True to form, over the development of numerous PHP applications, quite possibly you’ve been using some kind of design pattern, either because you needed to solve a specific problem by applying a well-known, proven solution, or simply due to your good coding habits. In either case, the good news is that you can become much more familiar with design patterns, as they can meet your programming requirements better than you may have initially thought.

Beyond the bunch of tongue-twisting buzzwords that “Singleton,” “Factory” or “Iterator” can be, the truth is that one — or all of them — have already been part of your productive developer life.

Certainly, among the most popular design patterns, there’s one that I think is especially interesting, particularly if you’re used to writing a lot of object-oriented Web applications. In this case I’m speaking of the Observer pattern. Indeed, this pattern can be extremely useful in situations where a certain number of objects (or components) must have a clearly-delimited area of responsibility inside an application, and at the same time, need to send information to a centralized mechanism about all their changes occurring at runtime.

This is very convenient in many situations. It is especially useful when it comes to working with Web-based user interfaces and system loggers. In these cases there are different independent objects that need to reflect any eventual changes by notifying a core module, which will take a course of action in accordance with the relevance of these modifications.

In the examples that I mentioned before, the core module “observes” the distinct objects and decides what course of action must be taken, in response to the different changes reflected by the observed objects. In all the cases, the objects in question are only limited to performing their specific tasks, and are not responsible for making decisions beyond their scope. This facilitates their decoupling from the rest of the application.

Okay, after reading this simple introduction, I hope you’ll be wondering how all this boring theory can be translated into functional PHP code. That’s what I’m going to do in the course of this article, by introducing some friendly examples of how to implement the Observer pattern in PHP applications.

Are you ready? Let’s get started.

{mospagebreak title=Building a practical example}

The introduction you read was aimed at providing you with an easy-to-grasp definition of the Observer pattern. As with many concepts related to PHP programming, things become clear only after satisfying the appetite for concrete code.

In consonance with this, I’ll start with a simple example, which defines two classes that manipulate PHP strings. Take a look at the corresponding class definitions:

As you can see, the two classes that I built above implement a simple mechanism that saves uppercased messages to a given text file. The first class, “MessageConverter,” is responsible for converting the input string passed as argument to uppercase, after which it returns the uppercased message to the calling code.

Regarding the “MessageSaver” class, its functionality is really simple. It takes up objects of type “MessageConverter” and saves their messages to a text file, after checking whether the messages in question contain prohibited words or are of an inappropriate length. In both cases, the class will trigger the corresponding exceptions, notifying of the pertinent errors.

Due to the simplicity of this example, let’s see how the above classes can be put to work quickly. Take a look at the following code snippet:

As shown above, the two classes seem to work seamlessly, and hopefully at the end of the script, the input message passed as a parameter will be saved in uppercase to the specified text file via the “save()” method. Also, it should be noted that these classes act like true independent components, since they have a well defined scope, and don’t overlap each other. What else can we ask for?

Well, if you’re going to keep this trivial message-saving application untouched for a long time, then take a deep breathe and congratulate yourself for building such an efficient system. However, say you want to add some kind of error logging module to the application, to keep track of all the errors that will occur when offending messages are passed to the “MessageSaver” class. Sounds like a simple addition, right?

In the next section I’ll show you how to add a basic error logger to the previous application, and hopefully you’ll see how the Observer pattern can be applied to solving some problems associated with the scope of the objects involved. Please keep on reading to learn more.

{mospagebreak title=Adding a basic error logger}

Adding a basic error logger to the message handling application that you learned before is in fact a straightforward process, which can be achieved by defining a simple error logging class. The code listed below illustrates how the application looks after including a new “ErrorLogger” class. Please, take a look:

Now, the message handling application’s functionality is increased, because I added to it a basic error logging class. As you can see, this class will log errors either to the system logger or by email to the corresponding system administrator, according to the type of error raised at runtime.

Based on the prior improved structure, the message handling application eventually might be used as follows:

At this point, I constructed a simple message saving system, which is comprised basically of three independent classes. It is capable of delegating program control to an error logger when things go wrong, in this way limiting the scope of each involved object.

Nevertheless, even when all the classes seem to fit each other quite well, there’s a problem that can easily break down the model of the whole application. Notice how the definition of the “MessageSaver” class has been convoluted with messy code, since the class now has to deal with the direct instantiation of an “ErrorLogger” object, in order to perform all the error login operations.

This is truly undesirable, particularly if I want to keep all the objects decoupled from the rest of the application. It gets even worse if I aggregate new classes to the application, while trying to maintain the correct scope of all the objects. Certainly, I would end up with a set of classes, which should perform the instantiation of other objects, quickly breaking down the model of highly independent classes.

Thus, the question is: can I restructure the message handling application in such a way that it can work with decoupled objects, while maintaining the same functionality? Fortunately, the answer is yes, and here’s where the Observer pattern comes in.

In the upcoming section, I’ll show you how to use this pattern to solve the issues that I discussed before. Therefore, click on the link below to learn more.

{mospagebreak title=Applying the Observer design pattern}

As I explained in the previous section, when a basic error login class was added to the message handling application, the independence of their objects was compromised; therefore, an alternative approach should be implemented to avoid this issue. For this reason, I’ll apply the Observer pattern, thus the application can retain the objects’ independence without sacrificing functionality.

Here’s how he application now looks, after implementing the Observer pattern:

As you can see, the above application looks slightly different when compared to the previous incarnation. All the changes that I introduced are minor yet significant, therefore pay close attention to the following explanation.

In order to apply the Observer pattern, first I defined a “MessageObserver” interface, which exposes only an abstract “displayStatus()” method. Next, I created a basic “ErrorLogger” class, which implements this interface and also inherits the previous method.

Here’s where you can spot the first difference. This class actually won’t log any errors, but instead will display notices from the respective “MessageSaver” objects. So, where’s the advantage in defining the logger class like this? Well, by doing so, all the objects of type “MessageSaver” will be responsible only for saving messages to a text file, period. Any eventual errors will be sent to the observer object (in this case the “ErrorLogger” class), and only this object will decide the course of action to take. Do you now see how all the involved objects are decoupled from the whole application? I hope you do.

Once the observer class was created, I redefined the “MessageSaver” class, in order to add the “addObserver()”,“removeObserver()” and “notifyObserver()” methods to it. As you may have guessed, the first two methods simply add and remove observer objects from the class in question, assuming that the application will implement eventually other observers.

The “notifyObserver()” method loops over all the observers (for this example, I have only one) and calls their “displayStatus()” method, in this way showing all the notices that were sent by the “MessageSaver” object when trying to save an offending message. This condition is demonstrated by the “save()” method, which is listed below:

At this point, the previous example allowed me to apply the Observer pattern, which results in the decoupling of all the pertinent objects. It also results in the implementation of a handy mechanism for reflecting all the objects’ changes at the application’s core level, and without having to write convoluted code!

Want to see how the application works, after coding the respective observer class? Right, here’s an example:

As you can see, the “MessageObserver” class displays the above notification, since I passed an invalid message to the “MessageConverter” class. This demonstrates the power of the Observer pattern.

As homework, try adding more observers to the “ErrorLogger” class, and see what happens in each case, so you gain a solid grasp of this pattern. It’s really fun!

To wrap up

In this first article, you learned how to apply the Observer pattern inside a simple PHP application. Of course, feel free to treat the source code like a starting example and introduce some improvements to it. It’s the best way to grasp the concepts for how this pattern works.

In the next tutorial, I’ll show you how to apply the Observer pattern in a real-world case: server-side data validation. See you there!