preface

Whether you have implemented the observer pattern or the listener pattern yourself in practice, you have certainly used it indirectly. Spring’s event mechanism, for example, is something most of us have probably used, but haven’t noticed.

Today’s article focuses on the observer pattern, the listener pattern, and the relationship between them. Not only will they be used with examples, but we’ll also talk about how Spring’s event mechanism works with the observer pattern.

How do listener mode and observer mode look the same?

Let’s talk about design patterns

Why use listening mode, is it not good to call directly? This brings us to the benefits of design patterns. Design pattern is a set of repeatedly used, most people know, cataloged code Design experience summary. Design patterns are used to re-use code, make it easier for others to understand, and ensure code reliability. At the same time, after adopting the design pattern, the code can achieve the effect of low coupling and low dependence.

That is, it can be hardcoded without design patterns. But if you consider the coupling, dependency, extensibility of your code, design patterns are a better choice. For example, observer mode can achieve decoupling, asynchrony, and so on.

Definition of observer pattern

The observer pattern defines one-to-many dependencies between objects, that is, a topic for multiple observers. When a topic object changes state, all of its dependencies (observers) are notified and updated automatically. For example, when a user subscribes to a subscription account or public account, new messages will be sent to all subscribers.

The observer pattern addresses the dependencies between objects. When multiple objects depend on an object’s relationship, a topic object’s state changes and all observer objects need to be notified.

The listener pattern is not a new design pattern, but a transformation and application of the observer pattern in specific scenarios. Typically, the topic of the observer mode does not contain any information when the observer is notified. If this process carries some additional information, then the topic itself becomes the source of the event, and the encapsulated class that carries the information becomes the event. The observer mode is then upgraded to a listener. The listener mode is another form of the observer mode.

Observer pattern instance

Let’s take a look at the code implementation of the Observer mode. You can either use the JDK’s own Observer or customize the corresponding API. The weight of this design pattern can also be seen in the JDK’s built-in observer pattern API (though deprecated in Java).

We use custom correlation classes, which mainly include subject and observer objects.

Start by defining an interface Subject for a topic:

Public interface Subject {/** * register */ void registerObserver(Observer Observer); /** ** Send notifications */ void notifyObservers(Object MSG); }Copy the code

The topic interface defines two methods: one for registering observers and one for sending notifications. ConcreteSubject Defines the concrete implementation class for this topic:

Public class ConcreteSubject implements Subject {/** * observers */ private List<Observer> observers = new ArrayList<>(); @override public void registerObserver(Observer Observer) {// Observers.add (Observer); } @override public void notifyObservers(Object MSG) {// Notify subscribers for (Observer Observer: observers) { observer.update(msg); }}}Copy the code

The implementation class stores a collection of observers, which implements a one-to-many relationship between a topic and an observer.

Create an Observer interface, Observer, for easy management:

Public interface Observer {// Handle business logic void update(Object MSG); }Copy the code

ConcreteObserver ConcreteObserver Class:

Public class ConcreteObserver implements Observer {@override public void update(Object MSG) System.out.println("ConcreteObserver receives message for topic: "+ MSG); }}Copy the code

In the implementation class, a line of messages is printed. Of course, in practice, this implementation class can be created as an anonymous class, so that the concrete anonymous class is defined at registerObserver.

Here’s a test:

public class ObserverTest { public static void main(String[] args) { Subject subject = new ConcreteSubject(); Observer observer = new ConcreteObserver(); // registerObserver subject.registerobserver (observer); // Publish messages about them. NotifyObservers (" messages from subject "); }}Copy the code

Execute the program and print the result:

The ConcreteObserver receives a message for the topic: a message from the SubjectCopy the code

Note You can normally receive messages published by the topic.

In the above implementation, you can see that you have achieved an understanding of coupling while reducing dependencies. Each observer does not need to know what business logic is being processed by the publisher, nor is it dependent on the publisher’s business model, and only cares about its own logic processing.

Listening mode instance

The listener pattern typically contains three roles: event source, event object, and event listener. Let’s see what happens if we change the class name and method in observer mode without changing the business logic.

For example, rename the Observer to Listener and change its update method to onClick(); Rename the Subject implementation class ConcreteSubject to ListenerSupport and rename the registerObserver method to addListener…

Define an Event object to pass Event information:

public class Event { private String data; private String type; Event(String data, String type) { this.data = data; this.type = type; } // omit getter/setter}Copy the code

The subscription object Observer of the original topic is renamed Listener and becomes Listener:

public interface Listener {
    void onClick(Event event);
}
Copy the code

Provide an implementation class for the listener, but in practice anonymous class creation is also possible:

Public class ListenerA implements Listener {@override public void onClick(Event Event) {system.out.println (" implements Listener, "); Type :" + event.getType() + ", data:" + event.getData()); }}Copy the code

The original theme ConcreteSubject becomes the event manager in contrast to ListenerSupport:

public class ListenerSupport { private List<Listener> listeners = new ArrayList<>(); public void addListener(Listener listener) { listeners.add(listener); } public void triggerEvent(Event event) { for (Listener listener : listeners) { listener.onClick(event); }}}Copy the code

Corresponding test code:

public class EventTest { public static void main(String[] args) { Listener listener = new ListenerA(); ListenerSupport listenerSupport = new ListenerSupport(); listenerSupport.addListener(listener); listenerSupport.triggerEvent(new Event("dataA", "typeA")); }}Copy the code

Execute the program and print the following information:

Trigger event, type:typeA, data:dataACopy the code

By comparing the above code, we can see that the renamed observer mode has changed to listener mode, even though the business logic remains the same. The associations are: event sources against ConcreteSubject (topic), event objects against Update method Object, and event listeners against ConcreteObserver (subscriber). This proves once again that the listener pattern is another form of the observer pattern.

Compare observer mode with listener mode

Use a diagram to compare the relationship and difference between observer mode and listener mode:

The advantage of the emitted listener mode by comparison is that in many scenarios, notifications come with other information that is essential, and events can encapsulate this information, giving it a polymorphic nature. Each event object can contain different information. In this sense, the event listener pattern is a further abstraction of the observer pattern.

Best practices in Spring

The classic use of the Observer pattern is the Spring event-driven model, which is implemented based on the observer pattern and is the most common event listener in a project.

The Observer pattern in Spring has four roles: event, event listener, event source, and event manager.

Event: ApplicationEvent is the parent class of all event objects. ApplicationEvent inherits from EventObject in the JDK. All events need to inherit ApplicationEvent and get the event source from source. Spring provides many built-in events, such as: ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent, ContextClosedEvent, RequestHandledEvent.

EventListener: ApplicationListener, also known as observer, inherits from EventListener in the JDK. There is only one method in this class, onApplicationEvent, which is executed when the event being listened for occurs.

Event source: ApplicationContext. ApplicationContext is the Spring core container. In event listening, ApplicationContext can act as the event publisher, that is, the event source. Because the ApplicationContext inherited from ApplicationEventPublisher. Defines the method of event publishing: in ApplicationEventPublisher publishEvent (Object event).

Event management: ApplicationEventMulticaster, used for event listener registration and broadcasting of events. It is used to register listeners by broadcasting events published by Applicationcontext to its list of listeners.

summary

As we know from this article, the essence of the listener mode is the observer mode. First, the callback function is registered with the observed object. When the observed object changes, the callback function is used to inform the observer/listener. Spring event management is also based on the observer mode, which is a classic case.

About the blogger: Author of the technology book SpringBoot Inside Technology, loves to delve into technology and writes technical articles.

Public account: “program new vision”, the blogger’s public account, welcome to follow ~

Technical exchange: Please contact the weibo user at Zhuan2quan