Observer design pattern is useful when you are interested in the state of an object and want to get notified whenever there is any change.
In observer pattern, the object that watch on the state of another object are called Observer and the object that is being watched is called Subject.
xxxxxxxxxx
public class MyTopic implements Subject {
private List<Observer> observers;
private String message;
private boolean changed;
private final Object MUTEX= new Object();
public MyTopic(){
this.observers=new ArrayList<>();
}
@Override
public void register(Observer obj) {
if(obj == null) throw new NullPointerException("Null Observer");
synchronized (MUTEX) {
if(!observers.contains(obj)) observers.add(obj);
}
}
@Override
public void unregister(Observer obj) {
synchronized (MUTEX) {
observers.remove(obj);
}
}
@Override
public void notifyObservers() {
List<Observer> observersLocal = null;
//synchronization is used to make sure any observer registered after message is received is not notified
synchronized (MUTEX) {
if (!changed)
return;
observersLocal = new ArrayList<>(this.observers);
this.changed=false;
}
for (Observer obj : observersLocal) {
obj.update();
}
}
@Override
public Object getUpdate(Observer obj) {
return this.message;
}
//method to post message to the topic
public void postMessage(String msg){
System.out.println("Message Posted to Topic:"+msg);
this.message=msg;
this.changed=true;
notifyObservers();
}
}
https://www.digitalocean.com/community/tutorials/observer-design-pattern-in-java
xxxxxxxxxx
// Java program to demonstrate working of
// onserver pattern
import java.util.ArrayList;
import java.util.Iterator;
// Implemented by Cricket data to communicate
// with observers
interface Subject
{
public void registerObserver(Observer o);
public void unregisterObserver(Observer o);
public void notifyObservers();
}
class CricketData implements Subject
{
int runs;
int wickets;
float overs;
ArrayList<Observer> observerList;
public CricketData() {
observerList = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer o) {
observerList.add(o);
}
@Override
public void unregisterObserver(Observer o) {
observerList.remove(observerList.indexOf(o));
}
@Override
public void notifyObservers()
{
for (Iterator<Observer> it =
observerList.iterator(); it.hasNext();)
{
Observer o = it.next();
o.update(runs,wickets,overs);
}
}
// get latest runs from stadium
private int getLatestRuns()
{
// return 90 for simplicity
return 90;
}
// get latest wickets from stadium
private int getLatestWickets()
{
// return 2 for simplicity
return 2;
}
// get latest overs from stadium
private float getLatestOvers()
{
// return 90 for simplicity
return (float)10.2;
}
// This method is used update displays
// when data changes
public void dataChanged()
{
//get latest data
runs = getLatestRuns();
wickets = getLatestWickets();
overs = getLatestOvers();
notifyObservers();
}
}
// This interface is implemented by all those
// classes that are to be updated whenever there
// is an update from CricketData
interface Observer
{
public void update(int runs, int wickets,
float overs);
}
class AverageScoreDisplay implements Observer
{
private float runRate;
private int predictedScore;
public void update(int runs, int wickets,
float overs)
{
this.runRate =(float)runs/overs;
this.predictedScore = (int)(this.runRate * 50);
display();
}
public void display()
{
System.out.println("\nAverage Score Display: \n"
+ "Run Rate: " + runRate +
"\nPredictedScore: " +
predictedScore);
}
}
class CurrentScoreDisplay implements Observer
{
private int runs, wickets;
private float overs;
public void update(int runs, int wickets,
float overs)
{
this.runs = runs;
this.wickets = wickets;
this.overs = overs;
display();
}
public void display()
{
System.out.println("\nCurrent Score Display:\n"
+ "Runs: " + runs +
"\nWickets:" + wickets +
"\nOvers: " + overs );
}
}
// Driver Class
class Main
{
public static void main(String args[])
{
// create objects for testing
AverageScoreDisplay averageScoreDisplay =
new AverageScoreDisplay();
CurrentScoreDisplay currentScoreDisplay =
new CurrentScoreDisplay();
// pass the displays to Cricket data
CricketData cricketData = new CricketData();
// register display elements
cricketData.registerObserver(averageScoreDisplay);
cricketData.registerObserver(currentScoreDisplay);
// in real app you would have some logic to
// call this function when data changes
cricketData.dataChanged();
//remove an observer
cricketData.unregisterObserver(averageScoreDisplay);
// now only currentScoreDisplay gets the
// notification
cricketData.dataChanged();
}
}
xxxxxxxxxx
import java.util.List;
import java.util.ArrayList;
import java.util.Scanner;
class EventSource {
public interface Observer {
void update(String event);
}
private final List<Observer> observers = new ArrayList<>();
private void notifyObservers(String event) {
observers.forEach(observer -> observer.update(event));
}
public void addObserver(Observer observer) {
observers.add(observer);
}
public void scanSystemIn() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
notifyObservers(line);
}
}
}
xxxxxxxxxx
Observer is a behavioral design pattern.
It specifies communication between objects: observable and observers.
An observable is an object which notifies observers about the changes in its state.
For example, a news agency can notify channels when it receives news.
Receiving news is what changes the state of the news agency, and it causes the channels to be notified.
Observer design pattern, there is a Subject that maintains the list
of Observers that are waiting for any update on the Subject. Once
there is an update in Subject it notifies all the observers for the
change.
E.g. In real life, students are waiting for the result of their test. Here
students are the observers and test is the subject. Once the result of
test is known, testing organization notifies all the students about
their result.
The most popular use of Observer pattern is in Model View
Controller (MVC) architectural pattern.
Main issue with Observer pattern is that it can cause memory leaks.
The subject holds a strong reference to observers. If observers are
not de-registered in time, it can lead to memory leak.
xxxxxxxxxx
public class EventManager {
Map<String, List<EventListener>> listeners = new HashMap<>();
public EventManager(String operations) {
for (String operation : operations) {
this.listeners.put(operation, new ArrayList<>());
}
}
public void subscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}
public void unsubscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.remove(listener);
}
public void notify(String eventType, File file) {
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.update(eventType, file);
}
}
}
xxxxxxxxxx
public interface IObserver
{
void Update(string message);
}
public class ConcreteObserver : IObserver
{
public string Name { get; }
public ConcreteObserver(string name)
{
Name = name;
}
public void Update(string message)
{
Console.WriteLine($"{Name} received the message: {message}");
}
}
public class Subject
{
private List<IObserver> observers = new List<IObserver>();
public void AddObserver(IObserver observer)
{
observers.Add(observer);
}
public void RemoveObserver(IObserver observer)
{
observers.Remove(observer);
}
public void NotifyObservers(string message)
{
foreach (var observer in observers)
{
observer.Update(message);
}
}
}
xxxxxxxxxx
// Subject (Observable) interface
public interface IWeatherStation
{
void AddObserver(IWeatherDisplay observer);
void RemoveObserver(IWeatherDisplay observer);
void NotifyObservers();
}
// Concrete subject (Observable) class that holds weather data
public class WeatherStation : IWeatherStation
{
private List<IWeatherDisplay> observers = new List<IWeatherDisplay>();
private float temperature;
private float humidity;
private float pressure;
public void AddObserver(IWeatherDisplay observer)
{
observers.Add(observer);
}
public void RemoveObserver(IWeatherDisplay observer)
{
observers.Remove(observer);
}
public void NotifyObservers()
{
foreach (var observer in observers)
{
observer.Update(temperature, humidity, pressure);
}
}
public void SetWeatherData(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
NotifyObservers();
}
}
// Observer interface
public interface IWeatherDisplay
{
void Update(float temperature, float humidity, float pressure);
}
// Concrete observer classes representing different weather displays
public class CurrentConditionsDisplay : IWeatherDisplay
{
public void Update(float temperature, float humidity, float pressure)
{
Console.WriteLine($"Current Conditions: Temperature {temperature}°C, Humidity {humidity}%, Pressure {pressure} hPa");
}
}
public class StatisticsDisplay : IWeatherDisplay
{
public void Update(float temperature, float humidity, float pressure)
{
Console.WriteLine($"Statistics: Max Temperature {temperature + 5}°C, Min Temperature {temperature - 5}°C");
}
}
public class ForecastDisplay : IWeatherDisplay
{
public void Update(float temperature, float humidity, float pressure)
{
if (pressure > 1000)
Console.WriteLine("Forecast: Expect clear skies.");
else
Console.WriteLine("Forecast: Expect rain.");
}
}
class Program
{
static void Main()
{
// Client code
WeatherStation weatherStation = new WeatherStation();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay();
StatisticsDisplay statisticsDisplay = new StatisticsDisplay();
ForecastDisplay forecastDisplay = new ForecastDisplay();
// Register display modules as observers
weatherStation.AddObserver(currentDisplay);
weatherStation.AddObserver(statisticsDisplay);
weatherStation.AddObserver(forecastDisplay);
// Simulate weather changes (Notify observers)
weatherStation.SetWeatherData(25.5f, 60.0f, 1012.0f);
}
}
xxxxxxxxxx
7. Observer Pattern:
The Observer pattern defines a one-to-many dependency between objects, so that when one object changes state, all its dependents are notified and updated automatically.
Example in React:
Suppose you want to create a simple notification system in your React app where multiple components can subscribe to updates.
import React, { useState } from 'react';
// Observer
const NotificationObserver = () => {
const [subscribers, setSubscribers] = useState([]);
const subscribe = (subscriber) => {
setSubscribers([subscribers, subscriber]);
};
const notify = (message) => {
subscribers.forEach((subscriber) => {
subscriber(message);
});
};
return { subscribe, notify };
};
// Components
const SubscriberComponent = ({ observer }) => {
const [message, setMessage] = useState('');
const receiveNotification = (message) => {
setMessage(message);
};
observer.subscribe(receiveNotification);
return (
<div>
<p>Received Notification: {message}</p>
</div>
);
};
const ObserverPatternExample = () => {
const notificationObserver = NotificationObserver();
return (
<div>
<SubscriberComponent observer={notificationObserver} />
<SubscriberComponent observer={notificationObserver} />
<button onClick={() => notificationObserver.notify('New message')}>Notify</button>
</div>
);
};
export default ObserverPatternExample;