This article source code: GitHub. Click here || GitEE. Click here
I. Observer Model
1. Concept Description
The observer pattern is the object's behavior pattern, also known as the Publish/Subscribe pattern. The observer pattern defines a one-to-many dependency that allows multiple observer objects to simultaneously listen to a subject object, which notifies all observer objects when the state changes. Redis and common message middleware publishing and subscribing modes are based on this principle.
2. Core role
- Abstract subject roles
The abstract subject role manages all observer objects in a unified way, and each topic can have one or more observers. An abstract topic provides an interface for adding and deleting observer objects. The abstract topic role is also called the Observable role.
- Specific Thematic Roles
Store the relevant state in the specific observer object; notify all registered observers when the internal state of the specific subject changes. Specific subject roles are also called specific observer roles.
- The role of abstract observer
Define an interface for all specific observers to update themselves when they are notified of the subject. This interface is called an update interface.
- Specific Observer Role
Specific observer roles implement update interfaces required by abstract observer roles in order to synchronize their own state with the state of the subject. If necessary, the specific observer role can maintain a reference to a specific subject object.
3. Source Code Implementation
- Message Push Mode
The subject object pushes the message of the subject to the observer, regardless of whether the observer needs it or not.
/** * Observer Design Patterns */ public class C01_Observer { public static void main(String[] args) { // Create subject objects ConcreteSubject subject = new ConcreteSubject(); // Creating Observer Objects Observer observer1 = new ConcreteObserver("Observer A"); Observer observer2 = new ConcreteObserver("Observer B"); // Registered observer subject.attach(observer1); subject.attach(observer2); // Modify the subject state subject.change("New State !"); /** * Topic Status: New State! *[Observer A: New State! *[Observer B) State: New State! */ } } // Abstract subject roles abstract class Subject { // Save registered observer objects private List<Observer> list = new ArrayList<>(); /** * Registered Observer Object */ public void attach (Observer observer){ list.add(observer); System.out.println("Register an observer:"+observer.getClass().getName()); } /** * Delete observer objects */ public void delete (Observer observer){ list.remove(observer); System.out.println("Delete an observer:"+observer); } /** * Notify all registered observers */ public void notifyObserver (String newState){ for (Observer observer : list) { observer.update(newState); } } } // Specific Thematic Roles class ConcreteSubject extends Subject{ private String state ; public String getState (){ return state ; } public void change (String newState){ state = newState; System.out.println("Theme state:"+state); //Notify each observer of a change in status this.notifyObserver(state); } } // The role of abstract observer interface Observer { /** * Update interface */ void update (String state); } // Specific Observer Role class ConcreteObserver implements Observer{ private String name ; // Observer state private String observerState ; public ConcreteObserver (String name){ this.name = name ; } /** * Update the state of the observer to align it with the state of the target */ @Override public void update(String state) { observerState = state ; System.out.println("["+this.name+"]state:"+observerState); } }
- Message pull mode
Subject objects convey a small amount of information when notifying the observer. If the observer needs the content of the message, it is equivalent to the observer pulling data from the subject object to get it from the observer.
The case is modified based on the above case. The observer obtains the topic of the subject, and only the topic of interest to himself, can he get the content further.
public class C02_Observer_Pull { public static void main(String[] args) { // Create subject objects ConcreteSubject1 subject = new ConcreteSubject1(); // Creating Observer Objects Observer1 observer1 = new ConcreteObserver1("Observer A","JAVA"); Observer1 observer2 = new ConcreteObserver1("Observer B","MySQL"); // Registered observer subject.attach(observer1); subject.attach(observer2); /* * Modify the subject state * Topic Status: JAVA State! * [Observer A Status: JAVA State! * Topic Status: MySQL State! * [Observer B) Status: MySQL State! */ subject.change("JAVA State !","JAVA"); subject.change("MySQL State !","MySQL"); } } abstract class Subject1 { // Save registered observer objects private List<Observer1> list = new ArrayList<>(); /** * Registered Observer Object */ public void attach (Observer1 observer){ list.add(observer); } /** * Delete observer objects */ public void delete (Observer1 observer){ list.remove(observer); System.out.println("Delete an observer:"+observer); } /** * Notify all registered observers about the topic of the incoming message */ public void notifyObservers (String msgTopic){ for (Observer1 observer : list){ observer.update(this); } } } class ConcreteSubject1 extends Subject1 { private String state ; private String msgTopic ; public String getState (){ return state ; } public String getMsgTopic (){ return msgTopic ; } public void change (String newState,String newMsgTopic){ this.state = newState ; this.msgTopic = newMsgTopic ; System.out.println("Topic status:"+state); this.notifyObservers(msgTopic); } } interface Observer1 { /** * Update interface * @param subject Pass in the subject object, and the aspect obtains the status of the corresponding subject object. */ void update(Subject1 subject); } class ConcreteObserver1 implements Observer1{ private String name ; // Topic selection private String msgTopic ; // Observer state private String observerState ; public ConcreteObserver1 (String name,String msgTopic){ this.name = name ; this.msgTopic = msgTopic ; } @Override public void update(Subject1 subject) { ConcreteSubject1 concreteSubject1 = (ConcreteSubject1)subject ; // Interest will be cancelled only if the topic is specified. if (concreteSubject1.getMsgTopic().equals(msgTopic)){ observerState = concreteSubject1.getState(); System.out.println("["+this.name+"]state:"+observerState); } } }
4. Comparison of Two Models
Push mode assumes that the subject object knows the data that the observer needs and pushes directly, which makes it difficult for the observer object to reuse. Pull mode is that the subject object does not know what data the observer needs, and will pass itself to the observer and take the value as needed.
II. Application of JDK
In the java.utill class library of JAVA language, an Observable class and an Observer interface are provided to support the observer pattern in JAVA language.
1. Observer interface
This interface defines only one method, the update() method, which is called by the notifyObservers() method of the observed object when its state changes.
package java.util; /** * A class can implement the <code>Observer</code> interface when it * wants to be informed of changes in observable objects. */ public interface Observer { /** * This method is called whenever the observed object is changed. An * application calls an <tt>Observable</tt> object's */ void update(Observable o, Object arg); }
2. Observable class
Observed classes are subclasses of the java.util.Observable class. Java.util.Observable provides methods to support observer objects.
- setChanged method: The state of the observer object has changed.
- notifyObservers: Call the update() method of all registered observer objects.
package java.util; public class Observable { private boolean changed = false; private Vector obs; /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector(); } /**Add an observer to the observer cluster*/ public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } /** Delete an observer from the observer cluster */ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } /** * If the object changes (then the hasChanged method returns true) * Call this method to notify all registered observers, calling their update() method * Input this and arg as parameters */ public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } /** Gather the observers and empty them */ public synchronized void deleteObservers() { obs.removeAllElements(); } /** Set "Changed" to true */ protected synchronized void setChanged() { changed = true; } /** Reset "changed" to false */ protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
3. Application cases
public class C03_Observer_JDK { public static void main(String[] args) { //Create the Observed Object MsgSource msgSource = new MsgSource(); //Create an observer object and register the observed object MsgConsumer watcher = new MsgConsumer(msgSource); msgSource.setData("Hello,Java"); msgSource.setData("Bye Java"); } } class MsgSource extends Observable { private String data = ""; public String getData() { return data; } public void setData(String data) { if(!this.data.equals(data)){ this.data = data; setChanged(); } notifyObservers(); } } class MsgConsumer implements java.util.Observer { // Adding observers public MsgConsumer(Observable msgSource){ msgSource.addObserver(this); } // State acquisition @Override public void update(Observable o, Object arg) { System.out.println("Message content:" + ((MsgSource)o).getData()); } }
Summary of Advantages and Disadvantages
The main function of the observer model is to decouple the object and isolate the observer from the observee.
Programs include multiple observers and multiple observers. Development and debugging are complex, and notifications of messages in Java are executed sequentially by default. Blocking the execution of an observer can affect the overall execution efficiency.
4. Source code address
GitHub·address https://github.com/cicadasmile/model-arithmetic-parent GitEE·address https://gitee.com/cicadasmile/model-arithmetic-parent