1. Definition of Observer mode:
Observer mode is also called subscription-publish mode, in which a target object manages all observer objects dependent on it and actively issues notifications when its state changes. This is usually achieved by calling each observer's methods. This pattern is usually used in event processing systems.
1. General structure of observer pattern
First, let’s take a look at the class diagram description of the observer pattern:
The roles of Observer Mode are as follows:
Subject (Abstract Topic Interface): Defines a series of operations on the observer list in the theme class, including addition, deletion, notification, etc.
Concrete Subject (specific topic class):
Observer (Abstract Observer Interface): Defines the observer to accept operations on the topic class update status.
ConcreteObserver (specific observer class): implements logic such as update topic class notifications by the observer interface.
From this class diagram, we can see that the theme class maintains a class list that implements the observer interface. The theme class uses this list to perform a series of additions, deletions and modifications on the observer. The observer class can also actively call the update method to understand the status update information of the theme class.
The above class diagrams describe only the basic observer pattern idea, and there are many shortcomings. For example, as an observer, you can also actively subscribe to certain topics, etc. The following examples will make some changes to apply specific business logic.
2. Observer mode example
We build an observer and topic class, where observers can actively subscribe to or cancel topics. Theme category is uniformly managed by a theme manager. The following is a class diagram:
Subject:
public interface Subject { //Register an observer public void register(Observer observer); //Remove an observer public void remove(Observer observer); //Notify all observers public void notifyObservers(); //Get the message to be published by the topic class public String getMessage();}ConcerteSubject:public class MySubject implements Subject { private List<Observer> observers; private boolean changed; private String message; //Object lock, used to synchronously update the observer list private final Object mutex = new Object(); public MySubject() { observers = new ArrayList<Observer>(); changed = false; } @Override public void register(Observer observer) { if (observer == null) throw new NullPointerException(); //Certify not to repeat if (!observers.contains(observer)) observers.add(observer); } @Override public void remove(Observer observer) { observers.remove(observer); } @Override public void notifyObservers() { // temp list List<Observer> tempObservers = null; synchronized (mutex) { if (!changed) return; tempObservers = new ArrayList<>(this.observers); this.changed = false; } for(Observer obj : tempObservers) { obj.update(); } } // Theme class publishes a new message public void makeChanged(String message) { System.out.println("The Subject makes a change: " + message); this.message = message; this.changed = true; notifyObservers(); } @Override public String getMessage() { return this.message; }}When ConcerteSubject makes an update, all observers in the list are notified, and the observer update method is called to implement the logic after receiving the notification. Note the synchronization block in notifyObservers here. In the case of multi-threading, in order to avoid adding and deleting the observer list by other threads when publishing notifications in the topic class, a temporary List is used in the synchronization block to obtain the current observer list.
SubjectManagement: Theme Class Manager
public class SubjectManagement { //A record name - Map private Map<String, Subject> subjectList = new HashMap<String, Subject>(); public void addSubject(String name, Subject subject) { subjectList.put(name, subject); } public void addSubject(Subject subject) { subjectList.put(subject.getClass().getName(), subject); } public Subject getSubject(String subjectName) { return subjectList.get(subjectName); } public void removeSubject(String name, Subject subject) { } public void removeSubject(Subject subject) { } //singleton private SubjectManagement() {} public static SubjectManagement getInstance() { return SubjectManagementInstance.instance; } private static class SubjectManagementInstance { static final SubjectManagement instance = new SubjectManagement(); }}The purpose of the topic manager is to obtain an instance object of the topic when the observer subscribes to a topic.
Observer:
public interface Observer { public void update(); public void setSubject(Subject subject);}ConcerteObserver:public class MyObserver implements Observer { private Subject subject; // get the notify message from Concentrate Subject @Override public void update() { String message = subject.getMessage(); System.out.println("From Subject " + subject.getClass().getName() + " message: " + message); } @Override public void setSubject(Subject subject) { this.subject = subject; } // subcirbe some Subject public void subscribe(String subjectName) { SubjectManagement.getInstance().getSubject(subjectName).register(this); } // cancel subscribe public void cancelSubscribe(String subjectName) { SubjectManagement.getInstance().getSubject(subjectName).remove(this); }} Test: We abstract subject classes and observers into writers and readers
public class ObserverTest { private static MySubject writer; @BeforeClass public static void setUpBeforeClass() throws Exception { writer = new MySubject(); //Add a writer named Linus SubjectManagement.getInstance().addSubject("Linus",writer); } @Test public void test() { //Define several readers MyObserver reader1 = new MyObserver(); MyObserver reader2 = new MyObserver(); MyObserver reader3 = new MyObserver(); reader1.setSubject(writer); reader2.setSubject(writer); reader3.setSubject(writer); reader1.subscribe("Linus"); reader2.subscribe("Linus"); reader3.subscribe("Linus"); writer.makeChanged("I have a new Changed"); reader1.update(); }}The above is a small example of the observer pattern. It can be seen that each topic class must maintain a corresponding observer list. Here, we can further abstract based on the abstract level of the specific topic, put this gathering into an abstract class to jointly maintain a list. Of course, the specific operation depends on the actual business logic.
2. Listener in Servlet
Let’s talk about Listener in Servlet, let’s talk about another form of the observer pattern - the event-driven model. Like the theme role of the observer pattern mentioned above, the event-driven model includes event sources, specific events, listeners, and specific listeners.
Listener in Servlet is a typical event-driven model.
There is a set of event-driven classes in JDK, including a unified listener interface and a unified event source. The source code is as follows:
/** * A tagging interface that all event listener interfaces must extend. * @since JDK1.1 */public interface EventListener {}This is a flag interface, and the JDK stipulates that all listeners must inherit this interface.
public class EventObject implements java.io.Serializable { private static final long serialVersionUID = 5516075349620653480L; /** * The object on which the Event initially occurred. */ protected transient Object source; /** * Constructs a prototype Event. * * @param source The object on which the Event initially occurred. * @exception IllegalArgumentException if source is null. */ public EventObject(Object source) { if (source == null) throw new IllegalArgumentException("null source"); this.source = source; } /** * The object on which the Event initially occurred. * * @return The object on which the Event initially occurred. */ public Object getSource() { return source; } /** * Returns a String representation of this EventObject. * * @return A a String representation of this EventObject. */ public String toString() { return getClass().getName() + "[source=" + source + "]"; }}EvenObject is a unified event source specified by JDK. The EvenObject class defines an event source and a get method to obtain the event source.
Let’s analyze the operation process of Servlet Listener.
1. The composition of Servlet Listener
Currently, there are 6 types of listener interfaces for two types of events in Servlets, as shown in the figure below:
The specific trigger situation is as follows:
2. A specific Listener triggering process
Let's take ServletRequestAttributeListener as an example to analyze the event-driven process here.
First of all, when HttpServletRequest calls the setAttrilbute method, it is actually called org.apache.catalina.connector.request#setAttrilbute method. Let's take a look at its source code:
public void setAttribute(String name, Object value) { ... //The above logic code has been omitted// Here the listener is notifyAttributeAssigned(name, value, oldValue); }The following is the source code of notifyAttributeAssigned(String name, Object value, Object oldValue)
private void notifyAttributeAssigned(String name, Object value, Object oldValue) { //Get the instance object of Listener defined in the webAPP from the container Object listeners[] = context.getApplicationEventListeners(); if ((listeners == null) || (listeners.length == 0)) { return; } boolean replaced = (oldValue != null); //Create related event object ServletRequestAttributeEvent event = null; if (replaced) { event = new ServletRequestAttributeEvent( context.getServletContext(), getRequest(), name, oldValue); } else { event = new ServletRequestAttributeEvent( context.getServletContext(), getRequest(), name, value); } //Tranquility through all listeners lists and find the listener for the corresponding event for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof ServletRequestAttributeListener)) { continue; } //Calling the listener method to implement the listening operation ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners[i]; try { if (replaced) { listener.attributeReplaced(event); } else { listener.attributeAdded(event); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t); // Error valve will pick this exception up and display it to user attributes.put(RequestDispatcher.ERROR_EXCEPTION, t); } } }The above example clearly shows how the ServletRequestAttributeListener is called. Users only need to implement the listener interface. Listeners in Servlets almost cover events that you are interested in throughout the entire life cycle of the Servlet. Flexible use of these Listeners can make the program more flexible.
3. Comprehensive examples
For example, if you have watched TVB's police and gangster movies, you will know how the undercover work. Generally, a policeman may have several undercover agents who sneak into the enemy and inquire about information. The undercover agent works entirely on the instructions of his leader. The leader says what action he must follow. If the action time changes, he must immediately change the time he cooperates with the action. If the leader sends two undercover agents to invade the enemy, then the leader is equivalent to an abstract theme. Inspector Zhang San sent two undercover agents Li Si and Wan Wang Wu. Zhang San is equivalent to a specific theme, and the undercover agent is equivalent to an abstract observer. These two undercover agents are Li Si and Wang Wu, which are specific observers. The action of the inquiry is equivalent to the observer registering the subject. Then this class diagram is as follows:
Using java API to implement the code description as follows:
package observer; import java.util.List; import java.util.Observable; import java.util.Observable; /** *Description: Police Zhang San*/ public class Police extends Observable { private String time ; public Police(List<Observer> list) { super(); for (Observer o:list) { addObserver(o); } } public void change(String time){ this.time = time; setChanged(); notifyObservers(this.time); } } package observer; import java.util.Observable; import java.util.Observer; /** *Description: Undercover A */ public class UndercoverA implements Observer { private String time; @Override public void update(Observable o, Object arg) { time = (String) arg; System.out.println("Uncover A received a message, and the action time is: "+time); } } package observer; import java.util.Observable; import java.util.Observer; /** *Description: Undercover B */ public class UndercoverB implements Observer { private String time; @Override public void update(Observable o, Object arg) { time = (String) arg; System.out.println("Uncover B received a message, and the action time was: "+time); } } package observer; import java.util.ArrayList; import java.util.List; import java.util.Observer; /** *Description: Test*/ public class Client { /** * @param args */ public static void main(String[] args) { UndercoverA o1 = new UndercoverA(); UndercoverB o2 = new UndercoverB(); List<Observer> list = new ArrayList<>(); list.add(o1); list.add(o2); Police subject = new Police(list); subject.change("02:25"); System.out.println("======================= Due to the information being exposed, the action time is advanced===============================================================================; subject.change("01:05"); } } Test run results:
Undercover B received the message, and the action time is: 02:25 Undercover A received the message, and the action time is: 02:25=============== Due to the exposure of the message, the action time is in advance ============ Undercover B received the message, and the action time is: 01:05 Undercover A received the message, and the action time is: 01:05
4. Summary
The observer pattern defines a one-to-many relationship between objects. When the state of an object (the observer) changes, the objects that depend on it will be notified. It can be applied to publish-subscribe, change-update-update in such business scenarios.
The observer uses a loose coupling method. The observer does not know the details of the observer, but only knows that the observer has implemented the interface.
The event-driven model is more flexible, but it also pays the cost of system complexity, because we have to customize a listener and event for each event source, which will increase the burden on the system.
The core of the observer model is to first distinguish the role and position the observer and the observer, and they are a many-to-one relationship. The key to implementation is to establish a connection between the observer and the observer. For example, there is a set in the observer class that is used to store the observer, and all observers must be notified when the detected thing changes. In the constructor of the observer, it will be passed in by the observer and will also register itself in the list of observers owned by the observer, that is, the observers list.
1. Advantages of Observer Mode:
(1) Abstract themes only rely on abstract observers (2) Observer mode supports broadcast communication (3) Observer mode separates the information generation layer and the response layer
2. Disadvantages of Observer Mode:
(1) If a topic is registered by a large number of observers, it will cost a higher price to notify all observers (2) If the response method of some observers is blocked, the entire notification process will be blocked, and other observers cannot be notified in time.