Behavioral Design Patterns provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily. Following design patterns come under this category.
Observer pattern defines a dependency between objects so that whenever an object changes its state, all its dependents are notified.
Observer pattern is used when there is one-to-many relationship between objects such as if one object is modified, its depenedent objects are to be notified automatically.
In observer design pattern multiple observer objects registers with a subject for change notification. When the state of subject changes, it notifies the observers.
Objects that listen or watch for change are called observers and the object that is being watched for is called subject.
You can think of observer design pattern in two ways
Key points of observer design pattern are
Lets' take the example of a news agency. A news agency gather news news and publish them to different subscribers. We need to create a framework for and agency to be able to inform immediately, when event occurs, its subscribers about the event. The subscribers can receive the news in different ways: Emails, SMS, etc. The solution need to be extensively enough to support new types of subscribers(maybe new communication technologies will appear).
There are four participants in the Observer pattern
update()
, which gets called when the Subject's state changes.notifyObservers()
method is used to update all the current observers whenever the state changes.The following sequence diagram illustrates the registration and notification flow in action.
When to use the Observer pattern
The Observer pattern is usually used in combination with other design patterns:
Now we'll look at an example of the observer pattern. We'll start by creating an interface for the subject called WeatherSubject
. This will declare three methods: addObserver()
, removeObserver()
, and doNotify()
.
Java
File WeatherSubject.java.
public interface WeatherSubject { public void addObserver(WeatherObserver weatherObserver); public void removeObserver(WeatherObserver weatherObserver); public void doNotify(); }
We'll also create an interface for the observers called WeatherObserver
. It features one method, a doUpdate()
method.
public interface WeatherObserver { public void doUpdate(int temperature); }
The WeatherStation
class implements WeatherSubject
. It is our subject class. It maintains a set of WeatherObservers
which are added via addObserver()
and removed via removeObserver()
. When WeatherSubject
's state changes via setTemperature()
, the doNotify()
method is called, which contacts all the WeatherObservers
with the temperature via their doUpdate()
methods.
File WeatherStation.java.
public class WeatherStation implements WeatherSubject { Set<WeatherObserver> weatherObservers; int temperature; public WeatherStation(int temperature) { weatherObservers = new HashSet<WeatherObserver>(); this.temperature = temperature; } @Override public void addObserver(WeatherObserver weatherObserver) { weatherObservers.add(weatherObserver); } @Override public void removeObserver(WeatherObserver weatherObserver) { weatherObservers.remove(weatherObserver); } @Override public void doNotify() { Iterator<WeatherObserver> it = weatherObservers.iterator(); while (it.hasNext()) { WeatherObserver weatherObserver = it.next(); weatherObserver.doUpdate(temperature); } } public void setTemperature(int newTemperature) { System.out.println("\nWeather station setting temperature to " + newTemperature); temperature = newTemperature; doNotify(); } }
WeatherCustomer1
is an observer that implements WeatherObserver
. Its doUpdate()
method gets the current temperature from the WeatherStation
and displays it.
File WeatherCustomer1.java.
public class WeatherCustomer1 implements WeatherObserver { @Override public void doUpdate(int temperature) { System.out.println("Weather customer 1 just found out the temperature is:" + temperature); } }
WeatherCustomer2
performs similar functionality as WeatherCustomer1
.
File WeatherCustomer2.java.
public class WeatherCustomer2 implements WeatherObserver { @Override public void doUpdate(int temperature) { System.out.println("Weather customer 2 just found out the temperature is:" + temperature); } }
The Demo
class demonstrates the observer pattern. It creates a WeatherStation
and then a WeatherCustomer1
and a WeatherCustomer2
. The two customers are added as observers to the weather station. Then the setTemperature()
method of the weather station is called. This changes the state of the weather station and the customers are notified of this temperature update. Next, the WeatherCustomer1
object is removed from the station's collection of observers. Then, the setTemperature()
method is called again. This results in the notification of the WeatherCustomer2
object.
public class Demo { public static void main(String[] args) { WeatherStation weatherStation = new WeatherStation(33); WeatherCustomer1 wc1 = new WeatherCustomer1(); WeatherCustomer2 wc2 = new WeatherCustomer2(); weatherStation.addObserver(wc1); weatherStation.addObserver(wc2); weatherStation.setTemperature(34); weatherStation.removeObserver(wc1); weatherStation.setTemperature(35); } }
Python 3
import abc class WeatherSubject(metaclass=abc.ABCMeta): @abc.abstractmethod def add_observer(self, weather_observer): pass @abc.abstractmethod def remove_observer(self, weather_observer): pass @abc.abstractmethod def do_notify(self): pass class WeatherObserver(metaclass=abc.ABCMeta): @abc.abstractmethod def do_update(self, temperature): pass class WeatherStation(WeatherSubject): def __init__(self, temperature): self.observers = [] self.temperature = temperature def add_observer(self, weather): self.observers.append(weather) def remove_observer(self, weather): self.observers.remove(weather) def do_notify(self): for observer in self.observers: observer.do_update(self.temperature) def set_temperature(self, temperature): print("Weather station setting temperature to %s" % temperature) self.temperature = temperature self.do_notify() class WeatherCustomer1(WeatherObserver): def do_update(self, temperature): print("Weather customer 1 just found out the temperature is: %s" % temperature) class WeatherCustomer2(WeatherObserver): def do_update(self, temperature): print("Weather customer 2 just found out the temperature is: %s" % temperature) if __name__ == '__main__': station = WeatherStation(33) wc1 = WeatherCustomer1() wc2 = WeatherCustomer2() station.add_observer(wc1) station.add_observer(wc2) station.set_temperature(34) station.remove_observer(wc1) station.set_temperature(35)