Tool and Resource Center

Help developers work more efficiently and provide tools and resources around the developer lifecycle

Developer.aliyun.com/tool?spm=a1…

One, from “red stop, green go” start

In the automotive industry, whether you are the wind of the autumn famous mountain car god, or a new job on the road killer, in front of the traffic lights need to abide by such an iron law – “red stop, green line.” When you get in the driver’s seat, you are destined to follow the lights.

In the above scenario, there are two characters — the traffic light and the driver. The driver needs to observe the change of the traffic light (that is, red or green), and make the corresponding driving measures (that is, walk or stop) according to the different change of color. This pattern of behavior between objects also exists in software design, which is the design pattern we will learn next – the observer pattern.

Second, basic concepts

Definition 1.

Observer Pattern is an object behavior design Pattern used to establish a dependency relationship between objects. It is defined as:

Define a one-to-many dependency between objects. When an object’s state changes, all dependent objects are automatically notified.

Two objects are specified in this definition:

  • Target object: A dependent or observed object that notifies all observer objects when its state changes. In the example above, the traffic light is the object of observation;

  • Observer object: a dependent object. When the observed object’s state changes, it is automatically notified and acts accordingly (or updates the corresponding state). In the example above, the driver is the observer;

Its structure diagram is as follows:

In addition, the observer Pattern is also known as publish-subscribe Pattern, model-view Pattern, source-listener Pattern, and so on.

2. Event-driven model based on observer pattern

In the actual programming process, we pay more attention to the occurrence of a certain event, such as the above mentioned traffic light turning red/green. After the traffic light changes color, the car will make corresponding measures (stop/start), which is the event-driven model. Also known as Delegation Event Model (DEM). There are three elements in the event-driven model:

  • Event source: the object on which the initial event occurs, and the object to be observed in the observer mode;

  • Event object: the event that is triggered, the event object needs to have a body that can execute the event, that is, the event source;

  • Event listener: an object that listens for events. When an event occurs on the corresponding object to be listened to, the event listener will take predefined measures according to the event.

The event-driven model mentioned above is actually implemented through the observer pattern. The following is the corresponding relationship between the observer pattern and the event-driven model:

As can be seen from the figure above, in the event-driven model, event listeners correspond to the observer objects in the observer mode. Event sources and events together constitute the observed and processed target objects, where event sources correspond to the observed target objects (that is, event listeners are registered with the event sources). Events that occur at the event source are objects that need to be handled by the event listener.

Events that occur on the event source are actually extensions of the action of state change of the target object in the observer pattern. A single state change cannot better meet the development needs, while events have better scalability.

Three, source inquiry

1. The Observer pattern in JDK

The Observer pattern is so common that the JDK has provided support for it since version 1.0. The JDK provides an Observable class, which provides a base-class implementation of the observed object, and an Observer interface, which provides a common processing interface for observers. By inheriting/implementing these two classes, development can easily implement the observer pattern.

NotifyObservers (Object ARG) are astute about Obserable, and are astute about notifyObservers(Object ARg).

Public void notifyObservers(Object arg) {// Observers. Object[] arrLocal; // Here we lock the target object to prevent thread-safe issues when obtaining the target object's state and observer collection. // However, there is no need to ensure thread-safety when notifying observers of corresponding processing. // In the current competitive situation, the worst outcome is as follows: // 1) a new observer will miss the local notification; // 2) Synchronized (this) {synchronized (this) {// judge whether the current target object's state has changed if (! changed) return; arrLocal = obs.toArray(); // Clear the status clearChanged(); } for (int i = arrLocal.length-1; i>=0; ((Observer)arrLocal[I]). Update (this, arg); }Copy the code

From this method, we can see that the notification to all observers needs to satisfy the necessary condition that the state of the target object changes. To ensure thread-safe access to the collection of states and observers, the synchronized keyword and local variables are used. However, the synchronization block does not include a call to the observer update method, which leads to the possibility of an observer not being notified or receiving an error notification.

The observer pattern to provide to the JDK, use process for: observables. SetChanged () – > observables. NotifyObservers (Object arg).

2. Event-driven model in JDK

In addition to the Observer pattern, the JDK implements support for event-driven models. To this end, the JDK provides the EventObject class and the EventListener interface to support this model. The former represents event objects in the event-driven model, while the latter represents event listeners.

First let’s look at the EventObject constructor:

public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");
        this.source = source;
 }
Copy the code

As you can see, a source object must be passed in the constructor, which is defined in the official comment as the object from which the event originally occurred. While this explanation may seem a little abstract at first glance, it might be better understood with the above example of traffic lights.

In the case of traffic lights, the traffic light is the source of the event, the change of light is the event, and the driver is the event listener. The actual object observed by the driver as the event listener is the traffic light. When the traffic light discoloration event occurs, the driver will handle the event accordingly (that is, handle the event).

According to the above logic, it is not difficult to see that the driver, the event listener, is actually registered with the traffic light event source, and then deals with the traffic light events. If we look at the EventListener interface provided by the JDK, we can see that we are declaring an interface without any methods in it. From a personal point of view, this may be because the author is concerned about the difficulty of everyone’s tastes, and instead of struggling to come up with a universal method, it is better to simply define an interface and let the user do whatever he or she wants.

2. Event-driven model in Spring — publish/subscribe pattern

The Spring framework has further clarified the data model for the event-driven model, and added the role of event publisher to the original concept, thus getting a new pattern — publish/subscribe pattern.

On the basis of the JDK, the Spring framework provides ApplicationEvent, ApplicationListener and ApplicationEventPublisher three base classes to support a publish/subscribe pattern. ApplicationEvent and ApplicationListener inherit EventObject and EventListener respectively, and their functions are the same as these two classes, so it will not be described too much. Here focus on specific ApplicationEventPublisher this new class, the introduction of the new incoming class corresponds to the above event driven model of event source this role, the difference in the JDK freedom is bold and unrestrained, it defines the event source to event publishers, and provides two methods:

@ FunctionalInterface public interface ApplicationEventPublisher {/ * * * notify all registered to the publishers of the listener to carry on the corresponding event handling * * @ param event Events for publishing, */ default void publishEvent(ApplicationEvent Event) {publishEvent((Object) event); } /** * notify all listeners registered with the publisher to handle the event ** @param event is used for publishing events. Any type of event can be handled */ void publishEvent(Object event); }Copy the code

As you can see, Spring provides both an event publishing method based on the ApplicationEvent type and event handling of the Object type to ensure extensibility and free rows. Here we select AbstractApplicationContext this ApplicationEvent base class to a look at the logic of events are published in the Spring:

@Override public void publishEvent(ApplicationEvent event) { publishEvent(event, null); } protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); // Wrap the event as ApplicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<>(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent<? >) applicationEvent).getResolvableType(); }} / / if possible, now immediately for multicast / / or once the initialization of multicast, idly for multicast the if (this. EarlyApplicationEvents! = null) { this.earlyApplicationEvents.add(applicationEvent); } else {/ / event broadcast, here is the key to the broadcasting getApplicationEventMulticaster () multicastEvent (applicationEvent, eventType); } // If (this.parent! = null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); Public void multicastEvent(final ApplicationEvent event, final ApplicationEvent event, final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType ! = null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<? > listener : getApplicationListeners(event, type)) { if (executor ! = null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); }}}Copy the code

Except for the event preparation process, broadcast the event to the listener, and then call the listener’s corresponding method. This process is basically the same as the Observable notification process seen above. However, unlike synchronous processing in the JDK, Spring uses thread pools to process events asynchronously, if any, to further decouple publishers and listeners.

Four,

The biggest specificity of the observer mode is that it establishes a one-to-many and loose coupling relationship, and the observation object only needs to maintain an abstract set of observers, without the need to perceive the specific observers. Such a loose coupling relationship is conducive to the observation target and the observer to carry out corresponding abstract processing, which well reflects the open closed principle.

Of course, the observer mode has its drawbacks. For example, it only defines a one-to-many relationship and cannot handle many-to-many scenarios. For example, you can only perceive that the object of observation has changed, but you can’t know how. These are scenarios or problems that the observer mode can’t handle.