The Observer Design Pattern "define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically" as per GOF. This pattern is widely used to implement distributed event handling systems, where an object (the subject) maintains a list of dependents (observers) that need to be notified of any state changes.
There are two actors involved in observer pattern:
- Publisher(Subject) : The publisher provides an interface for adding and removing observers from the publisher list of observers. It also contains a method for notifying observers. It notifies all observer whenever it's state changes.
- Observers : All observers implements an interface having a method to notify observer. Publisher published the update through this interface only. An observer will subscribe itself to publisher and will not monitor whether the state of publisher has changed or not.
- Publisher(subject) provides an Observer interface for the classes who wants to get notifies when state of the publisher changes.
- Publisher maintains a list of observers subscribed to it.
- Any number of observers can subscribe to Publisher.
- Observers can add or remove itself anytime.
- Either publisher can pass the new state information to observers while notifying them(push approach) or Observers can pull the data of their interest from publisher after getting notified by publisher.
Real world Example:- Magazine Subscription
Mark is a regular reader of a weekly tech magazine TechPhilia. He wants to read this magazine every week as soon as a new edition gets published. He subscribes to this magazine by visiting magazine publisher's website. Now, whenever a new edition of TechPhilia release the magazine subscriber automatically send him the latest edition of magazine. If he wants he can unsubscribe himself from magazine publisher's website to stop receiving magazines.
In above example, Magazine publisher is the subject and Mark is the Observer who is subscribed to Magazine publisher. Whenever a new magazine release(state of the subject changes), Mark gets the latest edition(Observer gets notified).
Advantages of Observer Design Pattern
- Flexible Associations : This technique advocates for flexible associations between subjects and observers, promoting a scenario of loose coupling. Subjects do not necessitate knowledge of the specific classes of their observers, thereby boosting flexibility and maintainability.
- Distinctive Division of Responsibilities : The monitoring technique enforces a clear division of responsibilities. Subjects concentrate on maintaining state, while monitors are dedicated to responding to state alterations. This division enriches the organization and readability of the code.
- Easily Extensible : Introducing new observers or subjects is uncomplicated, fostering an easily expandable system. The incorporation of new functionality can be achieved by implementing new monitor classes without altering existing code.
- Dynamic Relationships : The technique supports dynamic associations between subjects and observers. Observers can be included or excluded at runtime, facilitating dynamic adaptation to evolving requirements.
- Broadcast Communication : Observers automatically receive notifications when the subject's state undergoes changes. This automated notification system simplifies the implementation of distributed event-handling systems.
- Supports Event-Driven Programming : The monitoring technique aligns effectively with event-driven programming paradigms. It is frequently utilized to realize event-handling systems where numerous components respond to events initiated by a central entity.
- Encourages Reusability : Observer and subject classes can be repurposed in diverse contexts. This repurposing proves particularly advantageous when similar interaction patterns are required across different components.
When we should use Observer Pattern
- When multiple object needs the latest state information an object whenever it changes.
- When we have to maintain consistency in the state of the multiple objects. When one changes others must also change.
- When we want to add or remove observers dynamically on runtime.
- When we want to decouple publisher and observers implementation.
Implementation of Observer Design Pattern
Every observer must implement 'Observer' interface, If they want to subscribe to publisher's notifications.
Observer.javapublic interface Observer { public void update(int xCordinate, int yCordinate); }
Publisher provides an interface for adding and removing observers and a notification method.
Publisher.javapublic interface Publisher { public void addObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); }
LocationTransponder is the concrete implementation of Publisher. It maintains a list of observers subscribed to it. Whenever it's location changes it notify all observers.
LocationTransponder.javaimport java.util.ArrayList; public class LocationTransponder implements Publisher { private int xCordinate, yCordinate; private ArrayList<Observer> observerList; public LocationTransponder(){ observerList = new ArrayList<Observer>(); } public void addObserver(Observer o){ observerList.add(o); } public void removeObserver(Observer o){ observerList.remove(o); } public void notifyObservers(){ for(Observer o: observerList){ o.update(xCordinate, yCordinate); } } public void setLocation(int x, int y){ this.xCordinate = x; this.yCordinate = y; notifyObservers(); } }
LocationTracker and PathDrawer are implementation of Observer interface. Objects of both classes can subscribe to LocationTransponder publisher.
LocationTracker.javapublic class LocationTracker implements Observer { private int currentX, currentY; public void update(int xCordinate, int yCordinate){ this.currentX = xCordinate; this.currentY = yCordinate; System.out.println("Current location of publisher is (" + currentX + ", " + currentY + ")"); } }
PathDrawer.java
public class PathDrawer implements Observer { private int currentX, currentY; public void update(int xCordinate, int yCordinate){ if(currentX != xCordinate || currentY != yCordinate){ System.out.println("Draw Line from (" + currentX + ", " + currentY + ") to (" + xCordinate + ", " + yCordinate + ")"); } else { System.out.println("No Change in Position !!!!"); } this.currentX = xCordinate; this.currentY = yCordinate; } }
ObserverPatternExample is the client class which creates instance of LocationTransponder(publisher or subject) LocationTracker and PathDrawer(observers). It subscribe observers to LocationTransponder and changes the position of the LocationTransponder to notify observers.
ObserverPatternExample.javapublic class ObserverPatternExample { public static void main(String args[]){ LocationTransponder subject = new LocationTransponder(); Observer locationTracker = new LocationTracker(); Observer pathDrawer = new PathDrawer(); subject.addObserver(locationTracker); subject.addObserver(pathDrawer); // Changing state of the publisher(subject) subject.setLocation(2, 3); subject.setLocation(5, 10); subject.setLocation(5, 10); } }
Output
Current location of publisher is (2, 3) Draw Line from (0, 0) to (2, 3) Current location of publisher is (5, 10) Draw Line from (2, 3) to (5, 10) Current location of publisher is (5, 10) No Change in Position !!!!
Guidelines for Effective Implementation of the Observer Design Pattern
- Introduce interfaces for both the observer and subject. This guarantees that concrete monitoring entities and subjects conform to a standardized agreement, enhancing code modularity and extensibility.
- Shape the observer technique to accommodate multiple monitoring entities. This empowers a subject to inform a collective of observers when its state undergoes changes.
- Ensure that observers are not tightly coupled to specific subject implementations. This promotes flexibility and allows observers to subscribe to multiple subjects if needed.
- If observers or subjects are used in a multithreaded environment, consider adding thread safety mechanisms to ensure that notifications and updates are handled safely.
- Implement a mechanism for observers to unregister or be removed from the list of observers. This prevents memory leaks and unnecessary notifications to inactive observers.
- Clearly define the parameters and contract of the update() method in the observer interface. This makes it easier for concrete observers to understand how to handle updates.
- Exercise caution regarding the frequency of notifications. Steer clear of excessive notifications that might inundate observers with needless updates. Implement a strategy to notify observers solely when pertinent changes transpire.
- Encapsulate the state of the subject to regulate how and when observers are notified. This guarantees that observers receive consistent and meaningful updates based on the subject's state.
Decorator Design Pattern |
Facade Design Pattern |
Composite Design Pattern |
Singleton Design Pattern |
Builder Design Pattern |
List of Design Patterns |