Back to Thoughts On Technology Page | Back to Peter's Home Page | Back to Family Home Page



The Observer Design Pattern
Peter RosePeter Rose - 01/2005


ABSTRACT

HOW THE PATTERN WORKS

THE OBSERVABLE CLASS

THE OBSERVER INTERFACE

The ENGINE CLASS

The ENGINEMONITORTEMPERATURE CLASS

PUTTING THE PIECES TOGETHER

FULL CODE FOR OBSERVEREXAMPLE.JAVA

SAMPLE PROGRAM OUTPUT





Abstract
This paper will discuss the Observer Design Pattern as it applies to a simple example of an Engine class allowing an EngineMonitorTemperature class to monitor its operating tempreture. The beauty of the Observer Design Pattern is that the Observable Engine class does not need to know or be concerned with any of the details of being monitored by an Observer.

How The Pattern Works
The Observer Design Pattern allows one class - the Observable - to notify interested classes - called Observers - of events that occur in it.

The Observable class does not care about what the Observers do with this notification, or even if there are any interested Observers. Facility is simply provided in the design of the Observable class for this notification to take place if certain conditions in the Observable class occur.

For example, we will be looking at an Observable Engine class that will provide constant notification of its operation to anyone who chooses to be interested in monitoring it. We will create an EngineMonitorTemperature Observer class that will be responsible for monitoring that engine's operating tempreture and acting when the temperature exceeds the maximum allowed temperature. It will do this without the Engine needing to do or know anything about it.

The Observable Class
Java provides an Observable class in java.util that is used in conjunction with an Observer interface that is implemented by interested Observer classes.

An Observable class is one in which we may be interested in knowing what is going on, but we don't want to hard code in business logic for this as the logic requirements may change over time.

In addition, this business logic may not really have anything to do with the internal encapsulated behavior of that class - and thus really doesn't belong in it. For example, we may want to monitor an Engine class's tempreture and make certain business decisions based on the value of that tempreture - like shutting the engine down if the tempreture reaches a certain value. But this sort of decision really doesn't rest in the Engine itself.

From an encapsulated object persepctive: the engine should not have any knowledge about these types of decisions. It only knows how to run. As a result of running, its tempreture is going to vary based on the load required of it. It may know its maximum safe operating temperature but not necessarily what to do if that temperature is exceeded. And code to deal with this does not belong in the Engine class.

Nor does it belong in another class to which the Engine class instantiates and uses methods on that class to monitor itself. That couples the two classes together and would be in violation of a major tennate of the object model.

When designing a class that extends Observable, all that is necessary is to provide a private ArrayList to store all of the objects that want to observe it. The Observable doesn't need to know anything about these objects. It just has a list containing them and can simply cast them as Observable when it loops through them to call the Observer interface's implemented update() method.

The Observer Interface
Java provides an Observer interface in java.util for just the purpose of building decoupled business service objects from those objects they will monitor, i.e. the Observable object. It has but one method: update(Observable observable, Object obj).

Note that the Observable object that calls the Observer's update method passes the Observer an instance of itself. This gives the Observer access to the Observable's methods.

The Engine Class
The Engine class extends Observable because it is what we want to look at and monitor. For the purposes of making our Engine class as simple as possible, it will only have one instance property for its temperature temp, and a static final int for its max operating temperature. It will also have an ArrayList for storing Observer objects.


class Engine extends Observable {
	public static final int MAX_TEMP_ALLOWED = 383;
	private int temp = 0;
	private boolean okToRun = true;
	private ArrayList observers = new ArrayList();

There will need to be public methods to start and stop the engine.


	/* ====================================================
	 * Get the engine started.
	 * ==================================================== */
	public void startEngine() {
		while( okToRun ) {
			temp++;
		} //endwhile
	} //endmethod: startEngine

	/* ====================================================
	 * Stop the engine.
	 * ==================================================== */
	public void stopEngine() {
		setOkToRun(false);
	} //endmethod: stopEngine

Because the Engine class extends Observable, we will provide an overridden method addObserver() for adding an Observer object to the private ArrayList observers, and an overridden method notifyObservers() which is used to send a message to all registered Observer objects in that list.

Note that the startEngine() method could easily be rewritten as a Thread.

To make things simple, each iteration through the while loop increments the temperature property temp. So, it is most reasonable then in the design of this Observable object to put the notifyObservers() method here. And that's all the Engine class designer needs to know. The new startEngine() method will thus become


	/* ====================================================
	 * Get the engine started.
	 * ==================================================== */
	public void startEngine() {
		while( okToRun ) {
			temp++;
			notifyObservers();
		} //endwhile
	} //endmethod: startEngine

In a more complex system, there might be an EngineTemperatureGage object that is an aggregate to Engine. This class might reflect the temperature of the engine and could be passed to the Observer's update method as the 2nd parameter. In fact, there might be an EngineFuelGage as well that could be passed in from another location in the Engine class. The receiving Observer object might then be designed as an abstract class that implements a Command Design Pattern. It would thus pass the Observable object to the specific monitor for that notifyObservers() session. Thus, not only can many Observers be notified, but specifically only those observers meeting the cast of the passed 2nd parameter (see the discussion and defination of Observer's update() method).

The EngineMonitorTemperature Class
The EngineMonitorTemperature Class implements the Observer interface, and for our use is pretty simple. As is the case with most decoupled and highly encapsulated business classes, it has just a single public method: update(). It looks like this:


	/* ====================================================
	 * Only public interface to the outside world.
	 * ==================================================== */
	public void update(Observable aEngine, Object ignore) {
		checkEngineTemp((Engine)aEngine);
	} //endmethod: update()

Note the second parameter called ignore. We don't have to use this parameter if we don't need it. And in our example, we don't. But, as mentioned in the previous discussion of more complex systems, this second parameter could be an aggregate to the Observable object. It allows a more fine grained monitoring process, if needed. Writing the update() method to call the checkEngineTemp() method in this manner rather than doing the temperature check in update() allows this class to be more easily enhanced later if needed. There, in fact, may be numerous steps in checking the engine's temperature. Structuering the method in this way allows it to act as a Fašade Design Pattern to better coordinate the work. It's a basic logic refactor and thus is part of even the first design cut of the class.


	/* ====================================================
	 * Monitors Engine class's temperature.
	 * ==================================================== */
	private void checkEngineTemp(Engine aEngine) {
		if(aEngine.getTemp() > Engine.MAX_TEMP_ALLOWED) {
			aEngine.stopEngine();
		} //endif
	} //endmethod: checkEngineTemp

Putting The Pieces Together
Each class has been designed in very logical steps as to what each class's responsibilities are. Now we have to hook them together.

Of particular note is that "we" have to hook them together because they are decoupled and could not do this themselves. If this code was compiled and each class executed, nothing would happen. And that's a very good thing in object land to be able to say.

We need an ObserverExample.java class to pull the classes together. We can just put all of the code in its main() method in this manner:


public class ObserverExample {
	public static void main(String[] args) {
		Engine engine = new Engine();
		engine.addObserver(new EngineMonitorTemperature());
		engine.startEngine();
	} //endmain
} //endclass: ObserverExample.java

And that's all there is to it. Now, the classes can communicate with each other in a highly encapsulated and loosely coupled manner all in accord with the principals of the Object Model.

Full Code For ObserverExample.java



import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;


// ************************************************************************
/*
 * ObserverExample.java
 *
 * Provides a simple Observer Design Pattern Example.
 *
 * 

The example will show how to create an EngineMonitorTemperature * class that will be notified of events in an Engine class. */ // ************************************************************************ public class ObserverExample { /* ==================================================== * Main used as main driver for the example * ==================================================== */ public static void main(String[] args) { Logger.log(1, "Starting the Observer example..."); Engine engine = new Engine(); engine.addObserver(new EngineMonitorTemperature()); Logger.log(1, "Engine has accepted Observer: " + "EngineMonitorTemperature"); engine.startEngine(); } //endmain } //endclass: ObserverExample.java // ************************************************************************ class Engine extends Observable { public static final int MAX_TEMP_ALLOWED = 383; private int temp = 0; private boolean okToRun = true; private ArrayList observers = new ArrayList(); /* ==================================================== * Get the engine started. * ==================================================== */ public void startEngine() { while( okToRun ) { temp++; notifyObservers(); } //endwhile } //endmethod: startEngine /* ==================================================== * Stop the engine. * ==================================================== */ public void stopEngine() { Logger.log(1, "Shutting engine down..."); setOkToRun(false); } //endmethod: stopEngine /* ==================================================== * Adds an Observer object * ==================================================== */ public void addObserver(Observer aObserver) { observers.add(aObserver); } //endmethod: addObserver() /* ==================================================== * Notifies all its Observer objects of event. * ==================================================== */ public void notifyObservers() { Observer observer = null; for(int i=0; i < observers.size(); i++) { observer = (Observer)observers.get(i); observer.update(this,this); } //endfor } //endmethod: notifyObservers() public int getTemp() { return(temp); } private void setOkToRun(boolean aOkToRun) {okToRun = aOkToRun;} } //endclass: Engine // ************************************************************************ class EngineMonitorTemperature implements Observer { /* ==================================================== * Only public interface to the outside world. * ==================================================== */ public void update(Observable aEngine, Object ignore) { checkEngineTemp((Engine)aEngine); } //endmethod: update() /* ==================================================== * Monitors Engine class's temperature. * ==================================================== */ private void checkEngineTemp(Engine aEngine) { if(aEngine.getTemp() > Engine.MAX_TEMP_ALLOWED) { Logger.log(2, "EngineMonitorTemperature Alert: " + "Engine temp exceeds max allowed " + Engine.MAX_TEMP_ALLOWED); aEngine.stopEngine(); } //endif } //endmethod: checkEngineTemp } //endclass: Engine /* ======================================================================== * * Used for debug testing * * ======================================================================== */ class Logger { public static void log(int aLevel, String aMsg) { boolean showLogTraceToSystemOut = true; if(showLogTraceToSystemOut) { String prefix = ""; if(aLevel == 1) { prefix = " ---> "; } else if(aLevel == 2) { prefix = " ---> "; } else if(aLevel == 3) { prefix = " ---> "; } //endif System.out.println(prefix + aMsg); } //endif } //endmethod: log } //endclass: Logger


Sample Program Output



   ---> Starting the Observer example...
   ---> Engine has accepted Observer: EngineMonitorTemperature
     ---> EngineMonitorTemperature Alert: Engine temp exceeds max allowed 383
   ---> Shutting engine down...



Back to Thoughts On Technology Page | Back to Peter's Home Page | Back to Family Home Page