Life is too short to have a dog

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:

  • The target object: A dependent or observed object that notifies all observer objects of a state change. In the example above, the traffic light is the object of observation;
  • Observer object: Indicates a dependent object. When the status of the observed object changes, the object automatically receives a notification and acts accordingly (or updates the corresponding status). 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:

  • The event source: the object of the initial event, but also the object of observation in the observer mode;
  • The 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: Refers to the object that listens for events. When an event occurs on the corresponding object to be listened to, the event listener will take predefined corresponding actions 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) {
  			// A local variable to hold the observer collection
        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-case outcome is as follows:
  			// 1) A new observer will miss the local notification;
  			// 2) A recently logged out observer is incorrectly notified
        synchronized (this) {
          	// Check whether the current state of the target object has changed
            if(! changed)return;
            arrLocal = obs.toArray();
          	// Clear the status
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
          	// Notify all observers to perform the corresponding operation
            ((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 {

	/** * notifies all listeners registered with the publisher to handle the event **@paramEvent is a published event, where the event object must be the base class */ of ApplicationEvent
	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	/** * notifies all listeners registered with the publisher to handle the event **@paramEvent Is a published event. Any type of event can be processed */
	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
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		} else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast now, if possible
    // Or multicasts lazily once the multicaster is initialized
		if (this.earlyApplicationEvents ! =null) {
			this.earlyApplicationEvents.add(applicationEvent);
		} else {
      // Broadcast the event. Here is the key to broadcast
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish events through the context of the parent class
		if (this.parent ! =null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event); }}}/** * broadcast the event to the corresponding listener */
	public void multicastEvent(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.