preface
Well, the observer mode is very common in Android development, such as Adapter data update RecyclerView refresh, and four components Broadcast and so on. This article will briefly talk about observations and patterns in design patterns. For a better eating experience, this article combines a simple example to help you better understand the observer model.
I. Observer model
Let’s start with definitions: Define one-to-many dependencies between objects so that when an object changes state, all of its dependent objects are automatically notified. emmm… One-to-many dependencies? Dependent objects are notified of state changes. The definition is a bit tricky, so let’s just look at an example. When we were young, we all did eye exercises. When the music of eye exercises played on the radio, we began to do eye exercises. At this point, the radio and the students form a one-to-many dependency: when the radio plays the music of eye exercises, that is, when the status of the radio changes, we are notified to start doing eye exercises.
Roles in observer mode
Observer Pattern, as its name suggests, this design pattern has a role called “observer” and a role called “observed”. They are abstract concepts, so their concrete realizations are “concrete observer” and “concrete observed”. In the above example, “broadcast” plays the role of “observed”; The radio that sends the eye exercise music plays the role of the specific observed; The “student” plays the role of “concrete observer”; The role of “observer” is not obvious, it plays the role of interface. Example: Define an observer:
public interface Observer {
void doSomething(BroadCast broadCast);
}
Copy the code
Define the specific observer — Student
public class Student implements Observer {
private static final String TAG = Student.class.getSimpleName();
@Override
public void doSomething(BroadCast broadCast) {
if ("send Eye Exercises BroadCast! ".equals(broadCast.getBroadCast())) {
System.out.println(TAG + ": I will do eye exercises!");
} else {
System.out.println(TAG + ":I will be back home!");
}
}
}
Copy the code
Define the observed — BroadCast:
public abstract class BroadCast { private static final List<Observer> observers = new ArrayList<>(); public void register(Observer observer) { if (! observers.contains(observer)) { synchronized (BroadCast.class) { if (! observers.contains(observer)) { observers.add(observer); } } } } public void unRegister(Observer observer) { if (! observers.contains(observer)) { synchronized (BroadCast.class) { if (! observers.contains(observer)) { observers.remove(observer); } } } } protected void notifyAllObserver(){ Iterator<Observer> iterator = observers.iterator(); while(iterator.hasNext()){ Observer observer = iterator.next(); observer.doSomething(this); } } public abstract String getBroadCast(); public abstract void sendBroadCast(); }Copy the code
Define specific observed – eye exercises broadcast and school bell broadcast:
public class EyeExercisesBroadCast extends BroadCast { private static final EyeExercisesBroadCast INSTANCE = new EyeExercisesBroadCast(); private String broadCastMessage; private EyeExercisesBroadCast(){ } public static EyeExercisesBroadCast getInstance(){ return INSTANCE; } @Override public String getBroadCast() { return broadCastMessage; } @Override public void sendBroadCast() { broadCastMessage = "send Eye Exercises BroadCast! "; notifyAllObserver(); }}Copy the code
public class FinishClassBroadCast extends BroadCast { private static final FinishClassBroadCast INSTANCE = new FinishClassBroadCast(); private String broadCastMessage; private FinishClassBroadCast(){ } public static FinishClassBroadCast getInstance(){ return INSTANCE; } @Override public String getBroadCast() { return broadCastMessage; } @Override public void sendBroadCast() { broadCastMessage = "send finish class BroadCast! "; notifyAllObserver(); }}Copy the code
In addition, two auxiliary classes Main and Teacher are defined. Main is responsible for executing the code, while Teacher is the manager of the broadcast and can manage the transmission of the broadcast
public class Main { public static void main(String[] args) { Student student = new Student(); BroadCast broadCast1 = EyeExercisesBroadCast.getInstance(); BroadCast broadCast2 = FinishClassBroadCast.getInstance(); broadCast1.register(student); broadCast2.register(student); Teacher teacher = new Teacher(); }}Copy the code
public class Teacher { private static final String TAG = Teacher.class.getSimpleName(); private BroadCast[] broadCasts; public Teacher() { init(); System.out.println(TAG+": I send eye exercises broadcast! "); sendEyeExercisesBroadCast(); try { TimeUnit.SECONDS.sleep((int) (Math.random() * 6)); } catch (InterruptedException e) { } System.out.println(TAG+": I send finish class broadcast! "); sendFinishClassBroadCast(); } private void init() { broadCasts = new BroadCast[2]; broadCasts[0] = EyeExercisesBroadCast.getInstance(); broadCasts[1] = FinishClassBroadCast.getInstance(); } private void sendEyeExercisesBroadCast() { broadCasts[0].sendBroadCast(); } private void sendFinishClassBroadCast(){ broadCasts[1].sendBroadCast(); }}Copy the code
Run the program at this time, when the broadcast of eye exercise music, students will receive a notice, began to do eye exercise; When the bell rang over the radio, students were notified to start leaving school. The running results are as follows:
How the observer mode works
In Observer mode, the Observer looks at an Observable. There are a few key points to this process:
- Unlike most reality, observers do not actively observe, but passively receive notifications from observables
- An Observable needs to know who is observing it to notify it of state changes, so it maintains a List
- The Observer should have a callback method that passes in the Observable parameter so that the Observer can get the status of the Observable when it receives a notification
First of all, the Observer passively receives notifications from an Observable. Since one of the purposes of introducing the Observer mode is to avoid CPU consumption by polling, it is more appropriate to notify the Observer when the Observable state changes. Just like the example above, imagine the picture: students don’t have to keep asking the teacher: Is school over? Is school over? Is school over? . The teacher replied: No. No. No. . School is over. It’s hilarious, and it takes too much energy (CPU resources).
Secondly, since an Observable needs to notify the Observer, it needs to know who the Observer is. That is, it needs to store all Observer objects, so it needs to maintain a List<Observer>, The choice of data structure for List<Observer> depends on the actual situation. Take the example above: the radio (Observable) is installed in classrooms and hallways because it knows it is notifying students (otherwise it is installed in downtown areas where eye-exercise music suddenly blares and passers-by look black with question marks).
Finally, the Observer needs to know the Observable’s notification content, so the Observable puts the notification content in the callback function where the Observer gets the notification content. In the example above, students can get information about the end of school through the radio.
In addition, if there is a priority problem, List<Observer> can be ordered by priority, and then notified. If you add a bit of logic to the notification (inside the for loop), you can implement a function similar to the ordered broadcast in Android.
The argument list of the callback function
As mentioned in Part 3, the Observer can retrieve notification content through callback functions. But what is this notice about? Is it better to pass an Observable parameter or a message parameter? Or both of them? It is better to discuss separately.
4.1. Pass only Observable parameters
Just pass Observable parameters, easy, without a bunch of overloaded functions. But there are unexpected security issues: the Student class implements the Observer interface and can get the BroadCast object via doSomething(BroadCast), whereas the notifyAllObserver method of BroadCast is public! Even if we trust students not to repeat the broadcasts for pranks, the problem can still occur. From the Angle of programming in the code to run, if the observer call after receiving information BroadCast. NotifyAllObserver () method, is critical of the stack. For example, modify the student.dosomething (BroadCast) method:
@Override public void doSomething(BroadCast broadCast) { if("send Eye Exercises BroadCast! ".equals(broadCast.getBroadCast())){ System.out.println(this.getClass().getName()+": I will do eye exercises!" ); } else { System.out.println(this.getClass().getName()+":I will be back home!" ); } broadCast.notifyAllObserver(); }Copy the code
Added a line of broadCast here. NotifyAllObserver (), again the main method, the results are as follows:
Unfortunately, the stack burst. So we want to shield the notifyAllObserver method, and only BroadCast and other subclasses can touch it. So the notifyAllObserver method needs to be modified with the protected keyword, and its BroadCast subclasses need to be in the same package as BroadCast. The package structure is as follows:
The security problem can be solved by changing the access permission of notifyAllObserver().
4.2. Only the message parameter is passed
Message parameter only? It doesn’t seem to be a problem. But there may be more than one message! And an Observable doesn’t know what message the Observer needs. If multiple observers want to observe any one of n messages in an Observable, they may need different messages. Therefore, the number of callback functions must be 2n in order to satisfy all Observer requirements. Obviously this is not desirable, because the increase in overloaded functions reduces the maintainability of the program. At the same time, when the Observer observes multiple Observables, when one informs it, the Observer cannot know which one has updated, so only passing the message parameter has certain limitations.
4.3. Pass Both Observable and Message
As stated in 4.2, when there are more than one message, an exponential callback function is required. Therefore, it is best not to pass the message parameter in this case.
So, personally, the callback only needs to pass in an Observable parameter.
Five, the summary
A class may implement the Observer interface for “observing” functionality; A class implements the Observable interface/abstract class to obtain “Observable” properties. When the observer needs to observe the observed, it needs to instantiate an observed object and then call the observed’s registration method to register itself so that it can notify itself when the observed state changes. In my opinion, the observer mode essentially maintains a collection of callback functions. When the observed changes, the observed calls these callback functions one by one and the observer is notified. In addition, due to the nature of the observer mode, I think the observer mode is particularly suitable for asynchronous operations such as IO operations and network requests (downloading images and displaying them in ImageView, etc.).
Six, feeling
In fact, the observer mode was not the first design mode I came into contact with. The first design mode I came into contact with was the “Builder mode”. At that time, I was learning the custom View in Android development. Several constructor overloads and JavaBean patterns were not ideal, so the Builder pattern was designed, and with this scenario in mind, I understood why the Builder pattern was designed and how it worked. Back to the observer mode, when I was learning Android development, I used RecyclerView. At that time, I didn’t understand why its data update mechanism, and then I read this book “Android development From small workers to experts”. The original RecyclerView data update is adapter mode + observer mode! Combined with part of the source code in this book, I understood the data update mechanism of RecyclerView, and also understood the original intention of the observer mode. Therefore, design patterns should be combined with the actual scene to learn, in order to be easier to understand and use.