Main contents of this paper:

  • Introducing the Observer mode
  • Example of wechat public account publish/subscribe
  • Summary of observer model
  • Analyze the typical application of the observer model
    • The observer mode in the observer interface provided by the JDK
    • Observer mode in Guava EventBus
    • JDK delegates the observer pattern in the event model DEM
    • The observer pattern in the Spring ApplicationContext event mechanism

For more information, please visit my personal blog: Laijianfeng.org

Observer model

Observer mode is the “super mode” of design mode, and its application can be seen everywhere. Let’s take wechat public account as an example.

Wechat public accounts are divided into service accounts, subscription accounts and enterprise accounts. Take my public account for example, my public account type is subscription account, name is “little rotary front”, focus on big data, Java back-end class technology sharing. At present, we mainly share study notes and try our best to be “original”, “high quality” and “systematic”. Whenever I publish a blog push, subscribers can receive the push in time after I publish the push, so that they can easily read it on the mobile terminal.

Observer Pattern: Defines a one-to-many dependency between objects so that whenever an object’s state changes, its dependent objects are notified and automatically updated. The observer pattern is an object behavior pattern.

The aliases for the observer pattern include Publish/Subscribe, Model/View, Source/Listener, or Dependents.

The observer mode includes two types of objects: the observation target and the observer. A target can have any number of dependent observers. Once the state of the observation target changes, all the observers will be notified.

role

Subject: A target, also known as a Subject, is an object being observed. An observation target can accept any number of observers to observe. It provides a series of methods to add and remove observer objects. It also defines the notify method (). The target class can be an interface, an abstract class or a concrete class.

ConcreteSubject: A concrete target is a subclass of the target class. It usually contains frequently changing data and notifies its observers when its state changes. It also implements the abstract business logic methods (if any) defined in the target class. If the target class is not extended, the concrete target class can be omitted.

Observer: An Observer reacts to changes in the object of observation. An Observer is generally defined as an interface that declares the method update() for updating data. Therefore, it is called an abstract Observer.

ConcreteObserver: In the ConcreteObserver, a reference to the ConcreteObserver is maintained. It stores the ConcreteObserver states that correspond to the ConcreteObserver states. It implements the update() method defined in the abstract Observer Observer. It is usually implemented by calling the attach() method of the concrete target class to add itself to the target class collection or by detach() method to remove itself from the target class collection.

The sample

You first need a subscriber interface (observer) that has a receive method for receiving push notifications from public accounts

public interface Subscriber {
    int receive(String publisher, String articleName);
}
Copy the code

Then there is a wechat client (specific observer) that implements the Receive method

public class WeChatClient implements Subscriber {
    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public int receive(String publisher, String articleName) {
        // The action when a push is received
        System.out.println(String.format("User <%s> received a push from <%s> wechat official account with the title <%s>", username, publisher, articleName));
        return 0; }}Copy the code

Publisher class (target, observed), which maintains a list of subscribers, implements subscribing, unsubscribing, notifying all subscribers, and so on

public class Publisher {
    private List<Subscriber> subscribers;
    private boolean pubStatus = false;

    public Publisher(a) {
        subscribers = new ArrayList<Subscriber>();
    }

    protected void subscribe(Subscriber subscriber) {
        this.subscribers.add(subscriber);
    }

    protected void unsubscribe(Subscriber subscriber) {
        if (this.subscribers.contains(subscriber)) {
            this.subscribers.remove(subscriber); }}protected void notifySubscribers(String publisher, String articleName) {
        if (this.pubStatus == false) {
            return;
        }
        for (Subscriber subscriber : this.subscribers) {
            subscriber.receive(publisher, articleName);
        }
        this.clearPubStatus();
    }

    protected void setPubStatus(a) {
        this.pubStatus = true;
    }

    protected void clearPubStatus(a) {
        this.pubStatus = false; }}Copy the code

Wechat public account class (specific target), which provides the publishArticles method for publishing push, and calls the parent class’s notification to all subscribers method when the article is published

public class WeChatAccounts extends Publisher {
    private String name;

    public WeChatAccounts(String name) {
        this.name = name;
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s> wechat public account published a push, the article name is <%s>, the content is <%s>".this.name, articleName, content));
        setPubStatus();
        notifySubscribers(this.name, articleName); }}Copy the code

test

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("Little whirl");

        WeChatClient user1 = new WeChatClient("Zhang");
        WeChatClient user2 = new WeChatClient("Bill");
        WeChatClient user3 = new WeChatClient("Fifty");

        accounts.subscribe(user1);
        accounts.subscribe(user2);
        accounts.subscribe(user3);

        accounts.publishArticles("Design | observer model and typical application of"."Observer mode content...");

        accounts.unsubscribe(user1);
        accounts.publishArticles("Design patterns | singleton pattern and typical application"."Contents of singleton pattern...."); }}Copy the code

The result is as follows, which is in line with expectations. When the public account publishes a push, the users who subscribe to the public account can receive the push notification in time

Small XuanFeng < > WeChat public published a push, the article name is < | design mode observer pattern and typical application >, content is the content of the < the observer pattern... > user < zhang SAN > to receive < small XuanFeng > WeChat public push, article titled < | design mode observer pattern and typical application > user < li si > received < small XuanFeng > WeChat public push, Article titled < | design mode observer pattern and typical application > user < detective > received < small XuanFeng > WeChat public push, article titled < | design mode observer pattern and typical application > < small XuanFeng > WeChat public published a push, Article name is < design pattern | singleton pattern and typical application >, content is the content of the < singleton pattern... > > user <, dick, and harry received < small XuanFeng > WeChat public push, article titled < design pattern | singleton pattern and typical application > user < detective > received < small XuanFeng > WeChat public push, the article titled < design pattern | singleton pattern and typical application >Copy the code

The class diagram can be drawn as follows

Take this opportunity to do a small promotion, welcome to follow my wechat public number oh ^_^

Summary of observer model

The main advantages of the observer model are as follows:

  • The observer pattern can realize the separation of presentation layer and data logic layer, define the stable message update delivery mechanism, and abstract the update interface, so that a variety of different presentation layer can act as a concrete observer.

  • The observer pattern establishes an abstract coupling between the observed object and the observer. The object of observation only needs to maintain a collection of abstract observers, without knowing its specific observers. Since the object and observer are not tightly coupled, they can belong to different levels of abstraction.

  • Observer mode supports broadcast communication, and the observation target sends notifications to all registered observer objects, simplifying the design of a one-to-many system.

  • The observer mode meets the requirements of the “open and closed principle”, and it is convenient to add a new observer without modifying the original system code when there is no correlation between the observer and the observed object.

The main disadvantages of the observer model are as follows:

  • If an observation object has many direct and indirect observers, it can take a long time to notify all of them.

  • If there is a cyclic dependency between the observer and the observed target, the observed target can trigger cyclic calls between them, potentially causing the system to crash.

  • The observer mode has no corresponding mechanism to let the observer know how the observed object has changed, but only that the observed object has changed.

Applicable scenarios:

  • An abstract model has two aspects, one of which depends on the other. Encapsulate the two aspects in separate objects so that they can be changed and reused independently.

  • A change in one object will cause one or more other objects to change, without knowing exactly how many objects will change or who those objects are.

  • You need to create A chain of triggers in the system where the behavior of object A affects object B, and the behavior of object B affects object C… You can use observer mode to create a chain trigger mechanism.

Typical application of the observer pattern

The observer interface provided by the JDK

The Observer pattern plays an important role in the Java language. The Java.util package in the JDK provides the Observable class and the Observer interface that make up the JDK’s support for the Observer pattern.

Where the Observer interface is the Observer, there is only one update method, which is called when the observation target changes, and its code is as follows:

public interface Observer {
    void update(Observable o, Object arg);
}
Copy the code

The Observable class is the target class, which has more concurrency and NPE considerations than the Publisher class in our example

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs = new Vector();

    public Observable(a) {}// Use to register the new observer object into the vector
    public synchronized void addObserver(Observer var1) {
        if (var1 == null) {
            throw new NullPointerException();
        } else {
            if (!this.obs.contains(var1)) {
                this.obs.addElement(var1); }}}// Delete an observer object from the vector
    public synchronized void deleteObserver(Observer var1) {
        this.obs.removeElement(var1);
    }

    public void notifyObservers(a) {
        this.notifyObservers((Object)null);
    }
    // Notification method, which loops inside the method to call the update() method for each observer in the vector
    public void notifyObservers(Object var1) {
        Object[] var2;
        synchronized(this) {
            if (!this.changed) {
                return;
            }

            var2 = this.obs.toArray();
            this.clearChanged();
        }

        for(int var3 = var2.length - 1; var3 >= 0; --var3) {
            ((Observer)var2[var3]).update(this, var1); }}// To clear the vector, that is, to delete all observer objects in the vector
    public synchronized void deleteObservers(a) {
        this.obs.removeAllElements();
    }
    // This method is called and sets an internal tag variable of type Boolean changed to true, indicating that the state of the observed target object has changed
    protected synchronized void setChanged(a) {
        this.changed = true;
    }
    // Used to set the value of the changed variable to false, indicating that the object state has not changed or that all observer objects have been notified of their update() method
    protected synchronized void clearChanged(a) {
        this.changed = false;
    }
    // Returns whether the object state has changed
    public synchronized boolean hasChanged(a) {
        return this.changed;
    }
    // Returns the number of observers in the vector
    public synchronized int countObservers(a) {
        return this.obs.size(); }}Copy the code

We can use Observable class and Observer interface to re-implement wechat public account example.

WechatNotice added a notification class to deliver push notifications

@Data
@AllArgsConstructor
public class WechatNotice {
    private String publisher;
    private String articleName;
}
Copy the code

WeChatClient and WeChatAccounts are rewritten to implement the JDK Observer interface and inherit Observable class, respectively

public class WeChatClient implements Observer {
    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public void update(Observable o, Object arg) {
        //WeChatAccounts weChatAccounts = (WeChatAccounts) o;
        WechatNotice notice = (WechatNotice) arg;
        System.out.println(String.format("User <%s> received a push from <%s> wechat official account with the title <%s>", username, notice.getPublisher(), notice.getArticleName())); }}public class WeChatAccounts extends Observable {
    private String name;

    public WeChatAccounts(String name) {
        this.name = name;
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s> wechat public account published a push, the article name is <%s>, the content is <%s>".this.name, articleName, content));
        setChanged();
        notifyObservers(new WechatNotice(this.name, articleName)); }}Copy the code

The test, which differs from the test code in the example, is the method called

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("Little whirl");

        WeChatClient user1 = new WeChatClient("Zhang");
        WeChatClient user2 = new WeChatClient("Bill");
        WeChatClient user3 = new WeChatClient("Fifty");

        accounts.addObserver(user1);
        accounts.addObserver(user2);
        accounts.addObserver(user3);

        accounts.publishArticles("Design | observer model and typical application of"."Observer mode content...");

        accounts.deleteObserver(user1);
        accounts.publishArticles("Design patterns | singleton pattern and typical application"."Contents of singleton pattern...."); }}Copy the code

The test result is the same as the example

Small XuanFeng < > WeChat public published a push, the article name is < | design mode observer pattern and typical application >, content is the content of the < the observer pattern... > user < detective > to receive < small XuanFeng > WeChat public push, article titled < | design mode observer pattern and typical application > user < li si > received < small XuanFeng > WeChat public push, Article titled < | design mode observer pattern and typical application > user < zhang SAN > received < small XuanFeng > WeChat public push, article titled < | design mode observer pattern and typical application > < small XuanFeng > WeChat public published a push, Article name is < design pattern | singleton pattern and typical application >, content is the content of the < singleton pattern... > user < detective > to receive < small XuanFeng > WeChat public push, article titled < design pattern | singleton pattern and typical application > user < li si > received < small XuanFeng > WeChat public push, article titled < design pattern | singleton pattern and typical application >Copy the code

Observer mode in Guava EventBus

EventBus in Guava encapsulates a friendly production/consumption model that implements listener registration and event distribution in observer mode in a very simple way.

With Guava EventBus, if you want to Subscribe to a message, you don’t need to implement any interface, just add the @SUBSCRIBE annotation to the listener method. EventBus provides the register and unregister methods for registering and unregistering events. When EventBus calls the POST method, events are distributed to registered objects

Re-implement the example using Guava

@Data
@AllArgsConstructor
public class WechatNotice {
    private String publisher;
    private String articleName;
}

public class WeChatClient  {
    private String username;
    
    public WeChatClient(String username) {
        this.username = username;
    }

    @Subscribe
    public void listen(WechatNotice notice) {
        System.out.println(String.format("User <%s> received a push from <%s> wechat official account with the title <%s>", username, notice.getPublisher(), notice.getArticleName())); }}public class WeChatAccounts {
    private String name;
    private EventBus eventBus;

    public WeChatAccounts(String name) {
        this.name = name;
        this.eventBus = new EventBus();
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s> wechat public account published a push, the article name is <%s>, the content is <%s>".this.name, articleName, content));
        this.eventBus.post(new WechatNotice(this.name, articleName));
    }

    public void register(WeChatClient weChatClient) {
        this.eventBus.register(weChatClient);
    }

    public void unregister(WeChatClient weChatClient) {
        this.eventBus.unregister(weChatClient); }}Copy the code

test

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("Little whirl");

        WeChatClient user1 = new WeChatClient("Zhang");
        WeChatClient user2 = new WeChatClient("Bill");
        WeChatClient user3 = new WeChatClient("Fifty");

        accounts.register(user1);
        accounts.register(user2);
        accounts.register(user3);

        accounts.publishArticles("Design | observer model and typical application of"."Observer mode content...");

        accounts.unregister(user1);
        accounts.publishArticles("Design patterns | singleton pattern and typical application"."Contents of singleton pattern...."); }}Copy the code

Not surprisingly, the output is the same as in the previous two examples

Small XuanFeng < > WeChat public published a push, the article name is < | design mode observer pattern and typical application >, content is the content of the < the observer pattern... > user < zhang SAN > to receive < small XuanFeng > WeChat public push, article titled < | design mode observer pattern and typical application > user < li si > received < small XuanFeng > WeChat public push, Article titled < | design mode observer pattern and typical application > user < detective > received < small XuanFeng > WeChat public push, article titled < | design mode observer pattern and typical application > < small XuanFeng > WeChat public published a push, Article name is < design pattern | singleton pattern and typical application >, content is the content of the < singleton pattern... > > user <, dick, and harry received < small XuanFeng > WeChat public push, article titled < design pattern | singleton pattern and typical application > user < detective > received < small XuanFeng > WeChat public push, the article titled < design pattern | singleton pattern and typical application >Copy the code

Guava EventBus source code analysis can be found at t.cn/EZzC35B

JDK delegates the observer pattern in the event model DEM

Start by tapping an AWT button to listen to the Demo of the event

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;


public class MouseEvents {
    private Frame frame;
    private Button button;

    MouseEvents() {
        frame = new Frame("Clicking a button triggers a click event, and the console prints a log.");
        frame.setBounds(300.200.600.300);
        frame.setLayout(new FlowLayout());

        button = new Button("this is a button");
        button.setFont(new Font("Default".0.30));
        frame.add(button);

        dealwithEvent();

        frame.setVisible(true);
    }

    // Event listeners and event handlers
    private void dealwithEvent(a) {
        // Listen for a form closure event
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0); }}); button.addActionListener(new ActionListener() {
            private int eventCount = 1;

            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println(String.format("Action event occurred %d times", eventCount++)); }}); }public static void main(String[] args) {
        newMouseEvents(); }}Copy the code

Running the main method will bring up the following panel and button on the desktop

The button addActionListener adds the specified action listener to receive action events from the button. When the user presses or releases the mouse over the button, the JVM generates an event object of type ActionEvent. The button’s fireXXX() method (inherited from Component) is called when the event is triggered. Inside that method, the actionPerformed() method registered with the ActionListener object in the button (the anonymous event-handling class we implemented) is called. Implements the handling of events

One action event occurred two action events occurred three action events occurred four timesCopy the code

The observer pattern in the Spring ApplicationContext event mechanism

Spring’s event mechanism extends from Java’s event mechanism. Event handling in the ApplicationContext is provided by the ApplicationEvent class and the ApplicationListener interface. If a Bean implements the ApplicationListener interface and has been published to the container, the Bean will be notified each time the ApplicationContext publishes an ApplicationEvent

  • ApplicationContext: Event source in which the publishEvent() method is used to fire container events
  • ApplicationEvent: The event itself. Custom events need to inherit from this class and can be used to pass data
  • ApplicationListener: An event listener interface in which the business logic of an event is encapsulated

Re-implement the example using the Spring event mechanism

@Data
public class WechatNotice extends ApplicationEvent {
    private String publisher;
    private String articleName;

    public WechatNotice(Object source, String publisher, String articleName) {
        super(source);
        this.publisher = publisher;
        this.articleName = articleName; }}public class WeChatClient implements ApplicationListener {
    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof WechatNotice) {
            WechatNotice notice = (WechatNotice) event;
            System.out.println(String.format("User <%s> received a push from <%s> wechat official account with the title <%s>", username, notice.getPublisher(), notice.getArticleName())); }}public void setUsername(String username) {
        this.username = username; }}public class WeChatAccounts implements ApplicationContextAware {
    private ApplicationContext ctx;
    private String name;

    public WeChatAccounts(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s> wechat public account published a push, the article name is <%s>, the content is <%s>".this.name, articleName, content));
        ctx.publishEvent(new WechatNotice(this.name, this.name, articleName)); }}Copy the code

Create a spring. XML file in the Resources directory and fill in the following

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="WeChatAccounts" class="com.observer.sprintevent.WeChatAccounts" scope="prototype">
        <constructor-arg name="name" value=""></constructor-arg>
    </bean>
    <bean id="WeChatClient1" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="Zhang"></constructor-arg>
    </bean>
    <bean id="WeChatClient2" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="Bill"></constructor-arg>
    </bean>
    <bean id="WeChatClient3" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="Fifty"></constructor-arg>
    </bean>
</beans>
Copy the code

test

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        WeChatAccounts accounts = (WeChatAccounts) context.getBean("WeChatAccounts");
        accounts.setName("Little whirl");
        accounts.setApplicationContext(context);

        accounts.publishArticles("Design | observer model and typical application of"."Observer mode content..."); }}Copy the code

The output is as follows

Small XuanFeng < > WeChat public published a push, the article name is < | design mode observer pattern and typical application >, content is the content of the < the observer pattern... > user < zhang SAN > to receive < small XuanFeng > WeChat public push, article titled < | design mode observer pattern and typical application > user < li si > received < small XuanFeng > WeChat public push, Article titled < | design mode observer pattern and typical application > user < detective > received < small XuanFeng > WeChat public push, the article titled < | design mode observer pattern and typical application >Copy the code

In this example the actual type of object ApplicationContext ClassPathXmlApplicationContext, including main code associated with publishEvent method is as follows:

private ApplicationEventMulticaster applicationEventMulticaster;

public void publishEvent(ApplicationEvent event) {
    this.getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent ! =null) {
        this.parent.publishEvent(event); }}ApplicationEventMulticaster getApplicationEventMulticaster(a) throws IllegalStateException {
    return this.applicationEventMulticaster;
}

protected void initApplicationEventMulticaster(a) {
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
            this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        } else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton("applicationEventMulticaster".this.applicationEventMulticaster); }}Copy the code

The SimpleApplicationEventMulticaster as follows, MulticastEvent method mainly through traversal ApplicationListener (registered by AbstractApplicationEventMulticaster implementation), Using the thread pool framework Executor to execute the onApplicationEvent method of ApplicationListener concurrently is essentially consistent with the example

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    private Executor taskExecutor;

    public void multicastEvent(final ApplicationEvent event) {
        Iterator var2 = this.getApplicationListeners(event).iterator();

        while(var2.hasNext()) {
            final ApplicationListener listener = (ApplicationListener)var2.next();
            Executor executor = this.getTaskExecutor();
            if(executor ! =null) {
                executor.execute(new Runnable() {
                    public void run(a) { listener.onApplicationEvent(event); }}); }else{ listener.onApplicationEvent(event); }}}}Copy the code

Implement a simple event-driven SpringBoot event mechanism with Guava

Recommended reading

Design patterns and typical application of design patterns | | simple factory pattern factory method pattern and a typical application of design patterns | the abstract factory pattern and a typical application of design pattern model and typical application design | | builders prototype model and typical application design model and typical application design | | appearance Decorator pattern and a typical application of design pattern model and typical application design | | adapter the flyweight pattern and a typical application of design patterns | | portfolio model and typical application design pattern template method pattern and a typical application of design patterns | the iterator pattern and a typical application of design patterns | strategy pattern and typical applications Design patterns | observer pattern and typical applications