This week we’ll continue our exploration of design patterns and look at a commonly used behavioural pattern, the Observer pattern.
The Observer Pattern
The Gang of Four book defines the Observer pattern as follows:
“Define a one-to-many dependency between objects so that one object changes state, all its dependents are notified and updated automatically.”
The Observer design pattern is useful when we’re interested in the state of an object and want to be notified whenever there’s any change in its state.
One of the most common uses for the Observer pattern is when we code event listeners in applications with graphical user interfaces (GUIs).
Structure
The Observer pattern consists of the following participants:
- The object that watches the state of another object is called the Observer.
- The object being watched is called the Subject. A subject may have any number of observers. All observers are notified whenever the subject changes state.
The Observer pattern is also known as Publish-Subscribe. The subject is the publisher. It sends out notifications without having to know who its observers are. Any number of observers can subscribe to receive notifications.
Observer in the Java APIs
Here are some examples of the Observer pattern from the standard Java APIs:
- The Observer pattern is supported by the
Observer
andObservable
classes in thejava.util
package. However, these classes are not used very often in real world Java programming because theObservable
class must be subclassed. Java only supports single inheritance, so this becomes a problem if the subject class already extends another class. Observable
andObserver
have been deprecated in Java 9 because their event model is fairly limited. The order of notifications delivered byObservable
is unspecified, and state changes do not correspond one-to-one with the notifications. Theupdate()
method of theObserver
is passed anObservable
object. If we need to know what specific object it is, we have to implement our own code usinginstanceof
checking and type casting.- The better approach to implementing the Observer pattern is with events and listeners. A custom event can extend the
java.util.EventObject
class. Thejava.util.EventListener
tagging interface is used to define a listener class. Over 330 interfaces and classes in Java SE implement this interface. - The JEE Servlet API uses listeners to be advised of changes to the
ServletContext
, theServletRequest
and theHttpSession
. - JSF (JavaServer Faces) uses it in the
javax.faces.event.PhaseListener
. - The Spring
ApplicationContext
container implements theApplicationEventPublisher
interface which allows events to be published with thepublishEvent()
methods.
State Pattern: Example Code
Back in our first person shooter game, we’ve decided that when players are stuck in a dangerous situation, they could call for reinforcements. Obviously the reinforcements won’t arrive instantly; they may be helping another player, or be far away on the game map.
The player would just as obviously like to know when the reinforcements are on the way. We can just let the player keep on radioing in to headquarters to find out when help is on the way, which will take time and energy, and distract the player from defending themselves from the zombie hordes or werewolf packs.
A better solution would be to allow the player to register a call with HQ and be advised when to expect the reinforcements. This is where the Observer design pattern comes into its own.
The player would be the observer. Headquarters would have a number of observers, who they would notify whenever reinforcements are on their way.
We won’t use the Observer
and Observable
classes in this example. We’ll take the alternative approach of using events and listeners.
Let’s first define an event class for the reinforcements:
public class CavalryIsOnTheWayEvent extends java.util.EventObject { private String message; public CavalryIsOnTheWayEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } }
The superclass EventObject
provides a getSource()
method so that listeners will be able to get a reference to the object that sent the event. It’s not particularly important to know here because there is only one headquarters, but in later versions of the game, the reinforcements may be sent from other areas.
Next we need to create a listener interface that will be implemented by the player. THis interface will extend the java.util.EventListener
interface:
public interface CavalryListener extends java.util.EventListener { public void helpIsComing(CavalryIsOnTheWayEvent event); }
The HeadQuarters
class must handle listener registration and notifications internally. The Observable
class handle registration and notifications, but it needs to be subclassed. This would be a problem if the HeadQuarters
class extended another class, which could easily be the case as we extend the game.
public class HeadQuarters { private Listlisteners; public HeadQuarters() { listeners = new ArrayList<>(); // other constructor code } public void addCavalryListener(CavalryListener listener) { listeners.add(listener); } public void removeCavalryListener(CavalryListener listener) { listeners.remove(listener); } public void sendingOutReinforcements() { fireHelpIsComing("The cavalry is 10 minutes away!"); } protected void fireHelpIsComing(String message) { CavalryIsOnTheWayEvent event; event = new CavalryIsOnTheWayEvent(this, message); for (CavalryListener listener : listeners) { listener.helpIsComing(event); } } }
The Player
object is the listener/observer and needs to implement the CavalryListener
interface:
public class Player implements CavalryListener { // other code public void helpIsComing(CavalryIsOnTheWayEvent event) { System.out.println ("Help has been sent from " + event.getSource() ); System.out.println ("The message was " + event.getMessage() ); } }
This is how the code works in the client program:
HeadQuarters hq = new HeadQuarters(); Player player = new Player(); hq.addCavalryListener(player); hq.sendingOutReinforcements(30); hq.sendingOutReinforcements(20); hq.sendingOutReinforcements(10);
Related Patterns
The Observer pattern is used in the Model-View-Controller architecture. The Model is the Subject; the View is the Observer.
What’s next?
In the weeks ahead, we’ll continue examining some of the more useful design patterns. Stay tuned!
As always, please share your comments and questions.
You’ve probably heard of a functional interface before, often in the same breath as lambdas. What are functional interfaces, and why are they used with lambdas?
Lambdas and functional interfaces go hand in hand in Java.
A functional interface is simply an interface that has a single abstract method. Before Java 8, they were referred to as SAM interfaces.
Why are these interfaces so important that they have their own term? Two reasons:
- Good design.
The “I” in the SOLID design principles refers to the Interface Segregation Principle. This states that many small interfaces are better than one large general purpose interface. Clients shouldn’t be forced to implement interfaces they don’t use. So functional interfaces are the ultimate small interface. - Lambdas.
The only way the compiler can infer which method you’re trying to implement with a lambda expression is that there is only a single method that you can implement.
Quick recap
Let’s revisit the Comparator
example from the previous post.
Using the anonymous inner class code, it’s obvious what you’re doing: you’re implementing the compare()
method:
// use an anonymous inner class to create a Comparator object TreeSetts = new TreeSet<>( new Comparator () { @Override public int compare(Employee e1, Employee e2) { return e1.getAge() - e2.getAge(); } } );
But when you’re using a lambda expression, what can the compiler infer? The only way it can work out what you’re trying to do, is if you’re only allowed to implement a single method.
// use an lambda to create a Comparator object TreeSetts = new TreeSet<>( (Employee e1, Employee e2) -> { return e1.getAge() - e2.getAge(); } );
The Comparator
interface is a functional interface, with the compare()
method being the only abstract method.
@FunctionalInterface annotation
The @FunctionalInterface
annotation marks the interface as a functional interface. It forces the compiler to check that there is only one abstract method.
At first this seems unnecessary. Interfaces are usually very small Java classes and it should be easy to just see what’s in the interface. But from Java 8, we’re allowed to have implemented methods in an interface. These are static and default methods. If there are a lot of methods, it is difficult for a programmer to check that they only have one abstract method, or haven’t forgotten to implement a method appropriately.
For example:
@FunctionalInterface public interface MyInterface { public void foo(); // single abstract method // default instance method - can be overridden if needed public default void bar() { // appropriate code } // static class method - called as MyInterface.baz() public static void baz() { // appropriate code } }
Functional interfaces as targets for lambda expressions
As a general rule, you can’t reuse lambda expressions. If you pass a lambda expression to a method, you would usually copy-and-paste it if you wanted to use it again.
Functional interfaces to the rescue! Variables of functional interface types can be used as targets for lambda expressions. This means that you can assign a lambda expression to a variable, as long as that variable’s type is a functional interface.
Revisiting the Comparator
example, we can reuse the lambda expression easily if we assign it to a functional interface type:
Comparatorcmp1 = (Employee e1, Employee e2) -> { return e1.getAge() - e2.getAge(); } Comparator cmp2 = (e1, e2) -> { return e1.getAge() - e2.getAge(); } Comparator cmp3 = (e1, e2) -> e1.getAge() - e2.getAge(); TreeSet ts = new TreeSet<>(cmp3);
I’ll cover the default and static methods of Comparator
in a later post.