banner
jzman

jzman

Coding、思考、自觉。
github

Observer Design Pattern

Recently, I reviewed the knowledge related to design patterns, and I mainly studied the observer design pattern from the following aspects, as follows:

  1. What is the observer design pattern?
  2. Understanding key concepts.
  3. Ways to notify observers.
  4. Implementation of the observer pattern.
  5. Pros and cons of the observer pattern.
  6. 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.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.