Recently, I reviewed the knowledge related to design patterns, and I mainly studied the observer design pattern from the following aspects, as follows:
- What is the observer design pattern?
- Understanding key concepts.
- Ways to notify observers.
- Implementation of the observer pattern.
- Pros and cons of the observer pattern.
- Use cases.
What is the observer design pattern?#
The observer pattern is a software design pattern that defines a one-to-many relationship between objects. When the data of an object changes, it notifies other objects that depend on it to respond to the data changes. This design pattern, which has a one-to-many notification relationship when the data of the target object changes and the data of the corresponding observer object changes accordingly, is called the observer design pattern.
Understanding key concepts#
The observer design pattern mainly distinguishes between two concepts:
- Observer: Refers to the observer object, which is the subscriber of the message.
- Subject: Refers to the target object to be observed, which is the publisher of the message.
Ways to notify observers#
When the data of the subject changes, there are mainly two ways to notify the observers:
- Push: Notify the observers in a broadcast-like manner, and the observers can only passively and unconditionally receive the notification.
- Pull: Receive the notification from the subject and decide whether to retrieve the message independently.
Implementation of the observer pattern#
Below are two ways to implement the observer design pattern:
Implementing the observer pattern manually#
First, create the subject that the observers will observe, as follows:
/**
* The target object that observers observe
*/
public abstract class Subject {
protected ArrayList<Observer> observerList = new ArrayList<>();
// Indicates that the observer starts observing the target object (subject)
public void registerObserver(Observer obs) {
observerList.add(obs);
}
// Indicates that the observer cancels observing the target object (subject)
public void unRegisterObserver(Observer obs) {
observerList.remove(obs);
}
// When the state of the target object (subject) changes, update the state of the observer in a timely manner
public void notifyAllObserver(){
for (Observer observer : observerList) {
observer.update(this);
}
}
}
Create the concrete subject, as follows:
/**
* The concrete target object (subject)
*/
public class ConcreteSubject extends Subject{
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
// Notify other observers when the data changes
notifyAllObserver();
}
}
To facilitate uniformity, define the observer interface, as follows:
/**
* Observer interface
*/
public interface Observer {
void update(Subject subject);
}
Create the concrete observer, as follows:
/**
* The concrete observer
*/
public class ConcreteObserver implements Observer{
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
@Override
public void update(Subject subject) {
// Get the data changes of the target object and update the current observer accordingly
ConcreteSubject concreteSubject = (ConcreteSubject)subject;
state = concreteSubject.getState();
}
}
Finally, test the observer design pattern, as follows:
/**
* Main
*/
public class Client {
public static void main(String[] args) {
// Create the concrete target object (subject)
ConcreteSubject concreteSubject = new ConcreteSubject();
// Create multiple concrete observers
ConcreteObserver obs1 = new ConcreteObserver();
ConcreteObserver obs2 = new ConcreteObserver();
ConcreteObserver obs3 = new ConcreteObserver();
// Let the observers observe the data changes of the target object (subject)
concreteSubject.observerList.add(obs1);
concreteSubject.observerList.add(obs2);
concreteSubject.observerList.add(obs3);
// Change the data of a target object (subject)
concreteSubject.setState(10);
// Check if the observer data is consistent with the target object data changes
System.out.println("Observer obs1: " + obs1.getState());
System.out.println("Observer obs2: " + obs2.getState());
System.out.println("Observer obs3: " + obs3.getState());
}
}
Obviously, the execution result will be as follows:
Observer obs1: 10
Observer obs2: 10
Observer obs3: 10
By changing the data of the target object, the data of the corresponding observer is updated, achieving message subscription and sending.
Using the observer pattern provided by Java API#
The observer pattern provided by Java API is mainly implemented through Observer and Observable. First, create a class that extends Observable as the subject, as follows:
/**
* The subject (target object) to be observed
*/
public class ConcreteSubject extends Observable{
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
// Indicates that the data has changed
setChanged();
// Notify observers when the specific target object data changes
notifyObservers(state);
}
}
Then, create a class that implements Observer as the observer, as follows:
/**
* The observer
*/
public class ConcreteObserver implements Observer{
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
@Override
public void update(Observable arg0, Object arg1) {
ConcreteSubject concreteSubject = (ConcreteSubject) arg0;
// Update the data of the current observer based on the data changes of the target object (subject)
this.state = concreteSubject.getState();
}
}
Finally, test the observer design pattern, as follows:
/**
* Test the observer design pattern
*/
public class Client {
public static void main(String[] args) {
ConcreteSubject concreteSubject = new ConcreteSubject();
ConcreteObserver obs1 = new ConcreteObserver();
ConcreteObserver obs2 = new ConcreteObserver();
ConcreteObserver obs3 = new ConcreteObserver();
concreteSubject.addObserver(obs1);
concreteSubject.addObserver(obs2);
concreteSubject.addObserver(obs3);
concreteSubject.setState(100);
System.out.println("Observer obs1: " + obs1.getState());
System.out.println("Observer obs2: " + obs2.getState());
System.out.println("Observer obs3: " + obs3.getState());
}
}
Obviously, the execution result will be as follows:
Observer obs1: 100
Observer obs2: 100
Observer obs3: 100
Pros and cons of the observer pattern#
- Pros: The observer is abstractly coupled with the subject, and a stable message triggering mechanism can be defined.
- Cons: If the subject has multiple indirect observers, the message delivery will consume more time. If there is a circular dependency between the observer and the subject, it will eventually lead to system crashes.
Use cases#
The observer design pattern is widely used in development, mainly in the following scenarios:
- In processes such as games and chats, messages are forwarded from the server to the client.
- The observer pattern is used in the broadcast mechanism in Android and in ListView to notify data changes.
- Subscription-related systems, such as when the subscribed topic is updated, the subscriber will synchronize the subscribed articles.
This concludes the observer design pattern.