1. Necessity
1.1 Observer mode It is one of the patterns often used in oo design. When solving practical needs, the Observer pattern is often used. javase has provided the Observer interface and Observable class to enable you to implement the Observer pattern simply and quickly. Therefore, it is necessary to understand Observer and Observable;
2. Overview of observer model
2.1 role: observed object, observer
2.2 relationship:
1). Observed object: observer = 1: n
2). When the state of the observed object changes, all observers will be notified, and the observers will respond accordingly
3. Source code analysis
3.1 Observer interface
Observer is Java The source code of an interface under util package is as follows:
public interface Observer { void update(Observable o, Object arg); }
This interface specifies the behavior of the Observer. All observers need to respond accordingly when the observed Object changes. The specific response is to implement the update method of the Observer interface. When implementing the update method, you can use two parameters. One parameter is Observable and the other parameter is Object. Of course, if you implement an Observer mode scheme entirely by yourself, you may not design these two parameters when designing the Observer interface yourself. Then why does the jdk specify these two parameters when designing the interface? That is universality. Think about the whole Observer pattern. What classes need to interact with each other? When using this mode, three classes are involved, one is the Observer, one is the observed Object, and the other is the caller (the caller can be called by the observed Object itself, more often a specific business class). The current interface represents the Observer and needs to interact with the observed Object. Therefore, the update method needs to hold the reference of the observed Object, and the first parameter is generated; How to communicate with the caller is to add a parameter of type Object (this parameter is passed in when the caller calls the notifyObservers(Object obj) method of the Observable instance, of course, it can not be passed in); The first parameter can be said to provide a way for the Observer to pull data. Businesses in update can pull the information of the observed Object they want according to their needs (getters are generally provided in the observed Object). The second parameter is that the caller calls notifyObservers(Object obj) to push some information. Through these two parameters, the Observer, the observed Object and the caller (the observed Object itself may call the notification refresh method. At this time, there are only two observers and the observed) are connected.
3.2 Observable class
Observable is also Java For the interface under util package (of course), the member variables and methods of this class are as follows:
public class Observable { private boolean changed = false; private Vector<Observer> obs; public Observable(){}; protected synchronized void setChanged(){}; protected synchronized void clearChanged(){}; public synchronized void addObserver(Observer o){}; public synchronized void deleteObserver(Observer o) {}; public synchronized void deleteObservers(){}; public synchronized boolean hasChanged(){}; public synchronized int countObservers(){}; public void notifyObservers(){}; public void notifyObservers(Object arg){}; }
Let's start with member variables:
1) This class contains a boolean variable changed, which represents whether a change has occurred. The Observable class only provides this Boolean value to indicate whether a change has occurred, and does not define what change is. Because the specific definitions of change are different in each business, the subclass determines whether it has changed; This variable not only provides an abstraction (variable and invariant), but also provides a delayable loading of the observer's update status. Through the analysis of the notifyObservers method, we can know whether the observer will call the update method depends on the changed variable. Therefore, even if the observer changes logically, update will not be called as long as setChanged is not called. If we do not need to trigger update frequently in some business scenarios, we can call the setChanged method to delay the refresh in time.
2) This class contains a collection class Vector. The generic type of this class is Observer. It is mainly used to store the references of all objects observing itself, so that when updating, you can traverse the observers in the collection one by one and call the update method one by one
explain:
The jdk source code of 1.8 is Vector, and the source code of version is the collection implementation of ArrayList;
The inheritance system of Vector class is consistent with that of ArrayList. There are two main differences: first, Vector is thread safe, ArrayList is not thread safe, and Vector operation depends on adding synchronization keywords to methods to ensure thread safety. At the same time, the performance of ArrayList is better than that of Vector; Second, the expansion thresholds of Vector and ArrayList are different, and ArrayList saves more space than Vector;
Let's talk about the method:
1) The method of operating the changed variable is setChanged(), clearChanged(), hasChanged(); See the meaning of the name. The first is to set the change state and the second is to clear the change state. The access permissions of both methods are protected, indicating that these two methods are called by subclasses. Subclasses tell when the observed changes and when the changes disappear. The access permissions of haschanged () method are public, and callers can use this method. All three methods have synchronous keywords to ensure the thread safety of variable reading and writing operations.
2) The methods of operating the Vector type variable obs are addObserver(Observer o), deleteObserver(Observer o), deleteObservers(), countObservers(). These four methods can dynamically add observers, delete observers, delete all observers and obtain the number of observers. The access permissions of the four methods are public. This is a method provided to the caller to control which observers observe the observed object in real time and dynamically.
3) The four methods for operating Vector type variable obs are added with synchronization keywords. However, when we just analyzed the variable Vector obs of the member attribute, we said that the Vector type is thread safe. Why do the above four methods add synchronization keywords? What's the matter? According to my guess, it should be a legacy problem of programmer refactoring, because I said earlier that the source code of historical versions uses ArrayList to hold the reference of Observer, and ArrayList is not thread safe. Therefore, the method of combining the above four operations needs to add synchronization keywords to ensure thread safety, and later replaced with thread safe Vector, However, the methods of these four operation sets still retain the synchronization keyword.
4) Two external methods, notifyObservers(), notifyObservers(Object arg), are operated by the caller to notify all observers that an update operation is needed.
Without looking at the source code, think about what to do? Notifying the observers to perform the refresh operation is not to call the update method with the observers in the operation set of the for loop. This is not simple, so the version 1 is generated:
//Version one public void notifyObservers(Object arg) { if(changed){ for (int i = 0; i<obs.size(); i++) obs.get(i).update(this, arg); } }
Looking at version 1, it is easy to find that there are many problems with this method. First, the update method of all observers is called, but the observed change state is not cleared. Because the changed variable state is not reset, if notifyObservers is called multiple times, that is, the Observable is not changed again, the update method of all observers will be executed. Therefore, it needs to be modified as follows:
//Version 2 public void notifyObservers(Object arg) { clearChanged(); if(changed){ for (int i = 0; i<obs.size(); i++) obs.get(i).update(this, arg); } }
Look at version 2. There is still a problem. If concurrency occurs, the read and write operations of each thread on the changed variable are unsafe, and dirty reads may occur, resulting in repeated updates or no updates. Therefore, it needs to be modified as follows:
//Version 3 public synchronized void notifyObservers(Object arg) { clearChanged(); for (int i = 0; i<obs.size(); i++) obs.get(i).update(this, arg); }
Look at version 3. There will be no wrong operations caused by inconsistent variable states caused by concurrency. However, when there are a large number of observers or the execution time of the update method is long, the execution time of notifyObservers increases greatly and increases linearly after the observers change. For example, the number of concurrency is 20, At this time, 10 threads are changed and the notifyObservers method is called, then 10 threads will enter synchronization when executing this method. The rough calculation takes 10*for loop execution time, so it needs to be modified. We only lock the read-write part of the changed variable, which will not cause inconsistency in the variable state. At the same time, when the code of the synchronization block is executed, This thread can first execute a time-consuming for loop, which is modified as follows:
//Version 4 public void notifyObservers(Object arg) { synchronized (this) { if (!changed) return; clearChanged(); } for (int i = 0; i<obs.size(); i++) obs.get(i).update(this, arg); }
Looking at version 4, it is found that there are still problems. Although the consumption is solved, there will still be problems. Multi threads synchronize in the synchronization block, but when executing the for loop, the caller may continue to add and delete observers. If thread A has just executed i
//Source code scheme 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); }
The source code is really rigorous!!!
5) As mentioned above, the system's support for observer mode has so many advantages, but the following disadvantages are still inevitable:
A. Observable is a concrete implementation class, which is detail oriented rather than abstract oriented
B. You need to use inheritance when using Observable. Due to the single class inheritance of java, if your class has inherited a class, you cannot inherit Observable to implement the observer mode, and because setChanged and clearChanged methods are protected, you cannot complete the observer mode through combination
4. Application
Observer mode is a very common mode, especially in interface programming, such as BaseAdapter in android, which uses observer mode to notify the interface to redraw when the data source changes. Let's use the observer and Observable provided in the jdk to implement an example of observer mode.
Requirements:
A clown came to the town to perform a show for everyone. All viewers will respond accordingly according to whether the clown's performance is wonderful or not. For example, applaud if the performance is good, applaud if the performance is bad, and exit after the performance.
analysis:
This involves the Clown and the audience. The number of clowns is 1 and the number of audience is n. the audience will respond to the Clown's actions accordingly. This is very consistent with the scene of Observer mode. Therefore, we need to create a Clown class close as the observed object. Therefore, we need to inherit the Observable class and have the behavior of performance and exit at the same time; At the same time, you need to create an audience Viewer as an Observer, so you need to implement the Observer interface. At the same time, the audience has cheering behavior, upside down cheering behavior and exit behavior. Each audience also corresponds to a seat number.
realization:
import java.util.Observable; import java.util.Random; /** * @author puyafeng * @desc Clown class */ public class Clown extends Observable { /** Wonderful performance */ public static final int PERFORM_GOOD = 0; /** The performance was terrible */ public static final int PERFORM_BAD = 1; /** The performance is over */ public static final int PERFORM_COMPLETE = 2; /** * perform */ public void perform() { System.out.println("**The clown began to perform**"); int random = new Random().nextInt(2); //The clown's performance state is a random value. 0 performs well and 1 performs poorly switch (random) { case PERFORM_GOOD: System.out.println("**The clown is in good shape and performs very well!**"); break; case PERFORM_BAD: System.out.println("**The clown is in bad shape. He's out of a basket!**"); break; } setChanged(); notifyObservers(random);//The performance is passed to the second parameter of the audience's update method through this parameter } /** * At the end of the performance, the clown withdrew */ public void exit() { System.out.println("**The show is over, the clown exits!**"); setChanged(); notifyObservers(PERFORM_COMPLETE);//The exit message is passed to the second parameter of the viewer's update method through this parameter }
import java.util.Observable; import java.util.Observer; /** * @author puyf * @desc Audience class */ public class Viewer implements Observer { private int seatNo; public Viewer(int seatNo) { this.seatNo = seatNo; } @Override public void update(Observable o, Object arg) { Integer state = (Integer) arg; switch (state) { case Clown.PERFORM_GOOD: applause(); break; case Clown.PERFORM_BAD: CheerBack(); break; case Clown.PERFORM_COMPLETE: exit(); break; default: break; } } /** * applause */ private void applause() { System.out.println("Seat number" + getSeatNo() + "The audience applauded!"); } /** * Cheers */ private void CheerBack() { System.out.println("Seat number" + getSeatNo() + "The audience cheered!"); } /** * Exit */ private void exit() { System.out.println("Seat number" + getSeatNo() + "Audience exit!"); } public int getSeatNo() { return seatNo; } }
/** * * @author puyf * @desc Test class */ public class Test { public static void main(String[] args) { //Here comes a clown Clown clown = new Clown(); //The audience entered for (int i = 0; i < 5; i++) { Viewer v = new Viewer(i); clown.addObserver(v); System.out.println("The seat number is"+v.getSeatNo()+"The audience was seated"); } //The clown began to perform clown.perform(); //The clown's performance is over and out clown.exit(); } }
Execution results:
Audience with seat number 0 is seated
The audience with seat number 1 is seated
The audience with seat number 2 is seated
The audience with seat number 3 is seated
The audience with seat number 4 is seated
**The clown began to perform**
**The clown is in bad shape. He's out of a basket**
The audience at seat 4 cheered!
The audience at seat 3 cheered!
The audience at seat 2 cheered!
The audience at seat 1 cheered!
The audience at seat No. 0 cheered!
**The performance is over. Thank you for watching. Please exit**
Audience of seat No. 4 exit!
Audience of seat 3 exit!
Audience of seat 2 exit!
Audience of seat No. 1 exit!
Audience with seat number 0 exit!
Of course, the perform method and exit method in the Clown class above call notifyObservers(Object obj) method, sometimes call it in business logic, such as removing notifyObservers from perform and exit, and the main() method in our Test class calls the notifyObservers according to the specific business logic and introduces the parameters.
Original link: https://blog.csdn.net/u012250875/article/details/77747878