Observer Pattern — A Quick Guide
Design Patterns: Observer Pattern
--
“Observer Pattern defines one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.”
Aside from formal definition, Observer Pattern helps us to listen (subscribe) to a subject and be up-to-date whenever a change occurs.
Subjects are open for the Observers to subscribe, and does not know (nor care about) how they are implemented and vice versa.
Simple Example:
Suppose you are building a social media app and implementing it for multiple platforms.
a platform enum (for the sake of simplicity)
public enum Platform {
WEB(1),
MOBILE(2);
}
a view interface that has a display method
public interface View {
void display();
}
a subject interface that has add/delete observer and notify observers methods
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
an observer interface that has an update method
public interface Observer {
void update(Double x, Double y);
}
Let’s say you want to show currency information in our app. Based on incoming data, you’ll update the numbers on the screen.
a currency class that implements the methods in Subject interface
public class CurrencyData implements Subject {
private final List<Observer> observers = new ArrayList<>();
private Double value;
private Double interestRate;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
observers.forEach(observer ->
observer.update(value, interestRate));
}
public void setValue(Double value) {
this.value = value;
notifyObservers();
}
public void setInterestRate(Double interestRate) {
this.interestRate = interestRate;
notifyObservers();
}// getters}
Notice how notifyObservers() is called whenever a change occurs.
As your app grows larger, you may want to add new functionalities, such as weather information.
a weather class that implements the methods in Subject interface
public class WeatherData implements Subject {
private final List<Observer> observers = new ArrayList<>();
private Double temperature;
private Double humidity;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
observers.forEach(observer ->
observer.update(temperature, humidity));
}
public void setTemperature(Double temperature) {
this.temperature = temperature;
notifyObservers();
}
public void setHumidity(Double humidity) {
this.humidity = humidity;
notifyObservers();
}// getters}
a currency display class that implements the methods in Observer and View interfaces
public class CurrencyDisplay implements Observer, View {
private Platform platform;
private Double value;
private Double interestRate; public CurrencyDisplay(Platform platform, Subject subject) {
this.platform = platform;
subject.registerObserver(this);
} public void unsubscribe(Subject subject) {
subject.removeObserver(this);
} @Override
public void update(Double temperature, Double humidity) {
this.value = temperature;
this.interestRate = humidity;
display();
}
@Override
public void display() {
// display
}
}
a weather display class that implements the methods in Observer and View interfaces
public class WeatherDisplay implements Observer, View {
private Platform platform;
private Double temperature;
private Double humidity;
public WeatherDisplay(Platform platform, Subject subject) {
this.platform = platform;
subject.registerObserver(this);
} public void unsubscribe(Subject subject) {
subject.removeObserver(this);
} @Override
public void update(Double temperature, Double humidity) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
@Override
public void display() {
// display
}
}
Let’s see it in action:
CurrencyData currency = new CurrencyData();
WeatherData weather = new WeatherData();
CurrencyDisplay webCurrency = new CurrencyDisplay(WEB, currency);
CurrencyDisplay mobileCurrency
= new CurrencyDisplay(MOBILE, currency);
// currency information is available for both platformsWeatherDisplay mobileWeather = new WeatherDisplay(MOBILE, weather);
// weather information is available for mobile,
// not available for webdouble temperature = 21.05;
weather.setTemperature(temperature);
// for weather -> only mobile is notified
double interestRate = 14.18;
currency.setInterestRate(interestRate);
// for currency -> both web and mobile got notifiedmobileCurrency.unsubscribe(currency);
// for currency -> mobile is not listening anymoreinterestRate = 15.20;
currency.setInterestRate(interestRate);
// for currency -> only web is notified
In Observer Pattern, the Subject is an open book. Observer is free to subscribe and unsubscribe based on its needs.
If you want to unsubscribe, it’s so easy! Since this design is loosely coupled, you can just call unsubscribe(subject) and it’s all done!
Loosely coupled designs allow us to build flexible object-oriented systems that can handle change. They minimize the interdependency between objects.
Real-Life Example:
Observer Pattern is widely used in javax.swing package.
Let’s think of a service that will update both the displays and the database information based on change of data:
UserData data = new UserData();Display webDisplay = new Display(WEB, data);
Display mobileDisplay = new Display(MOBILE, data);Database postgresql = new Postgresql(data);
Database couchbase = new Couchbase(data);// web display, mobile display, postgresql and couchbase
// all subscribed to user data.
Button is the subject here, when the button is clicked, data gets changed.
button.addActionListener((ActionListener)
ae -> data.setEmail(email));
When data gets changed, all observers get notified and updated.
Thank you for reading and being a part of my journey!
Reference(s)
- Head First Design Patterns. by Eric Freeman, Elisabeth Robson, Bert Bates, Kathy Sierra. Released October 2004. Publisher(s): O’Reilly Media, Inc.