The Observer pattern is a software design pattern in which an object, called the subject, maintains a list of all the other objects that depend on it (the subject). These dependents are called observers. They are automatically notified if the state of the subject (the maintainer of all dependents(observers)) changes. It is usually accomplished by a method call.
When to use the Observer Pattern
- Usually used when there is one-to-many relationship between objects.
- When an object needs to be updated if another (related) object modifies/changes its ‘behaviour’.
- When the subject’s number of observers is unknown.
How it works
Usually, there is a Subject.java file that either contains all of the methods that do the adding, the deleting and the updating of all observers or just initializes them and does all of the method functionality in different .java file. By convention, there is also a Observer.java file that holds an update() method that is called every time the Subject changes. After that you have to create .java files that implement either the Subject class or the Observer class. Please note that it varies on whether your classes should implement Subject or Observer class file.
Real-life example
Think of your favourite membership service. Maybe it’s the gym, or maybe it’s Netflix or maybe it’s something completely different. We are going to be using Netflix for this example but basically any other membership service would work. If Netflix changes its price for the monthly membership and you are paying for it, then it should notify you. In fact, it should notify all of the registered/subscribed people to their services.
Java Observer Design Pattern Example
Subject.java
public interface Subject { public double getCurrentPrice(); public void setNewPrice(double newPrice); public void addNewSubscriber(Observer newSuObserver); public void removeSubscriber(Observer unsubscriber); public void notifyAllSubscribers(); }
In Subject.java we will be initializing our methods that we will be using in SubscriptionGrabber.java file which I will introduce to you shortly. These methods handle the adding, deleting and the message that gets triggered whenever the state of the subject changes.
Observer.java
public interface Observer { public void update(double price); }
This is our Observer class. It has the update(double price) method which we will create in the class SubscriptionObserver.java file. Please note: This method will be called every time the price of the subscription changes.
SubscriptionGrabber.java
import java.util.*; public class SubscriptionGrabber implements Subject { private List<Observer> subscribers = new ArrayList<Observer>(); private double price; public SubscriptionGrabber() { subscribers = new ArrayList<Observer>(); } public double getCurrentPrice() { return price; } public void setNewPrice(double newPrice) { this.price = newPrice; notifyAllSubscribers(); } public void addNewSubscriber(Observer newSubscriber) { subscribers.add(newSubscriber); } public void removeSubscriber(Observer unsubscriber) { int indexOfUnsubscriber = subscribers.indexOf(unsubscriber); subscribers.remove(indexOfUnsubscriber); } public void notifyAllSubscribers() { for (Observer subscriber : subscribers){ subscriber.update(price); } } }
This .java file uses the Subject interface. Note in the setNewPrice(double newPrice) method that we notify the users after we have set the new price. It is important that we do it after the price change as later this could be a problem if we have more code chunks in there. Our addSubscriber(Observer newSubscriber) and removeSubscriber(Observer unsubscriber) are simply adding or removing a person to/from the observers List. We need to have a remove functionality in our pattern as our aim is to notify only the subscribers, or in other words only the people that use our services. We have no need to notify the people that are longer using our services. And finally, the final method in this .java file simply calls the update(double price) method (which we still have not created) on EVERY SINGLE subscriber.
SubscriptionObserver.java
public class SubscriptionObserver implements Observer { private double price; private Subject subscriberGrabber; private int subscriptionID; public SubscriptionObserver(Subject subscriptionGrabber) { this.subscriberGrabber = subscriptionGrabber; this.subscriptionID+=1; subscriberGrabber.addNewSubscriber(this); System.out.println("New Observer: " + this.subscriptionID); } public void update(double price) { System.out.println("Price changed from $" + this.price + " to $" + price); this.price = price; } }
This is the .java file that actually (finally) has the update function. Note how I put a print statement within it. It is very important (having a print statement within an update method) as you need to visually display what has happened to the user. In the constructor method, the parameter is always an empty list (you will see in the next .java file) and the subscriptionGrabber will be our new list that will hold the subscribers. On line 9, you can see we are adding this new subscriber.
ObserverPattern.java
public class ObserverPattern { public static void main(String[] args) { SubscriptionGrabber subscriptionGrabber = new SubscriptionGrabber(); SubscriptionObserver subscriptionObserver = new SubscriptionObserver(subscriptionGrabber); SubscriptionObserver subscriptionObserver2 = new SubscriptionObserver(subscriptionGrabber); SubscriptionObserver subscriptionObserver3 = new SubscriptionObserver(subscriptionGrabber); subscriptionGrabber.setNewPrice(5); } }
This is where we create the grabber (the list) and then we pass that list as a parameter in the subscription observer variable as a constructor parameter. And then we set the new price of the subscription to be $5. The expected result will be to notify all users that the price has indeed changed to $5. Let’s try it!
It worked as expected. Now let’s try to create 2 more subscriptions and see if the message is going to pop up 3 times (because we will be having 3 subscriptions).
It did print it three times.
View Comments (1)
interface Subject shouldn't contain:
public double getCurrentPrice();
public void setNewPrice(double newPrice);
The Subject must be more abstract and NOT describe methods like setNewPrice(), getCurrentPrice().
This redundant and must be in another class.