Observer design pattern

Posted by whitelion on Wed, 22 Sep 2021 16:10:37 +0200

preface

Starting from this section, we will formally enter the study of concurrent programming design pattern. First, we will talk about the observer design pattern. We will start with the java design pattern, and then transition to the observer pattern of concurrent programming

1, What is the observer design pattern?

About what is the observer model? My understanding is this. First of all, the observer model has the role of observer. Since there is an observer, there is an observed. Then this model emphasizes the word observer. That is, the observer observes every move of the observed in real time. Once the state of the observed changes, all observers will act accordingly. After searching the relevant information on the Internet, I have a deeper understanding of it: the observer is also known as the publish subscriber model. The publisher is the object of observation, and the subscriber is the observer.

2, Application scenario

The most typical application scenario is micro-blog subscribing and WeChat official account. Once the micro-blog or WeChat public address is released, new messages will be released, and the official account will see the release of the little message. Publishing news is actually a notification process.

3, java observer pattern example

1. Observed (publisher)

The publisher is the event source. The observed should first have the attributes of the observer, which can be a single or a list. Then the publisher should have a status attribute, which can be set or obtained. The observer will be notified when the state of the observed changes. To notify the observer, the observed must have a notification method. The most important thing is that the observed person should have a registration method, which is simply the subscribed method, so that the subscriber can subscribe to the publisher. The specific codes are as follows

package observer;

import java.util.ArrayList;
import java.util.List;

public class Subject {
    private Integer state;

    private List<Observer> observerList = new ArrayList<>();

    public Integer getState() {
        return this.state;
    }

    public void setState(Integer state) {
        if (this.getState().equals(state)) {
            return;
        }
        notifyObServer(state);
    }

    public void attach (Observer observer) {
        this.observerList.add(observer);
    }

    private void notifyObServer(Integer state) {
        this.state = state;
        observerList.forEach(Observer::update);
    }


}

2. Observers (subscribers)

There can be multiple observers, so you need to use interfaces or abstract classes to realize decoupling. When an observer is created, you should register yourself with the publisher (subscription process: add yourself to the observer list of the observed), and then the observer will respond after being notified by the publisher, so the observer should have a response method.

package observer;

public abstract class Observer {

    protected Subject subject;

    Observer(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }

    public abstract void update();
}


package observer;

public class BinaryObserver extends Observer {
    public BinaryObserver(Subject subject) {
        super(subject);
    }

    @Override
    public void update() {
        System.out.println("binary String: " + Integer.toBinaryString(subject.getState()));
    }
}

package observer;

public class OctalObserver extends Observer {

    public OctalObserver(Subject subject) {
        super(subject);
    }

    @Override
    public void update() {
        System.out.println("octal String: " + Integer.toOctalString(subject.getState()));
    }
}

3. Client

package observer;

public class client {
    public static void main(String[] args) {
        Subject subject = new Subject();
        new BinaryObserver(subject);
        new OctalObserver(subject);
        System.out.println("======================");
        subject.setState(10);
        System.out.println("======================");
        System.out.println("======================");
        subject.setState(10);
        System.out.println("======================");
        System.out.println("======================");
        subject.setState(12);
        System.out.println("======================");
    }
}

3, Application of java observer pattern in multithreading

1. Observed threads

Here we define an abstract class that implements Runnable. As above, as an observer, you must first have the attributes of the observer, which can be one or more. Here, let's be simple and use the one-to-one relationship. Secondly, there should also be a registration method, which is slightly different from the above. Here, when you choose to create a new Runnable, register the observer, that is, implement it in the construction method. Finally, there should also be a notification method.

package threadobserver;

/**
 * Requirements: observe the life cycle of threads
 * 1,Observable Runnable
 * 2,Thread lifecycle observer (interface and implementation classes)
 */
public abstract class ObserverableRunnable implements Runnable {
    /**
     * 1,Define observer
     * 2,Define notification method
     */
    private final LifecycleListener listener;

    public ObserverableRunnable(final LifecycleListener listener) {
        this.listener = listener;
    }

    public void notifyObserver(final RunnableEvent event) {
        this.listener.onEvent(event);
    }

    enum RunnableState {
        RUNNING, DONE, ERROR;
    }

    class RunnableEvent {
        private RunnableState state;
        private Thread thread;
        private Throwable cause;

        public RunnableEvent(RunnableState state, Thread thread, Throwable cause) {
            this.state = state;
            this.thread = thread;
            this.cause = cause;
        }

        public RunnableState getState() {
            return state;
        }

        public void setState(RunnableState state) {
            this.state = state;
        }

        public Thread getThread() {
            return thread;
        }

        public void setThread(Thread thread) {
            this.thread = thread;
        }

        public Throwable getCause() {
            return cause;
        }

        public void setCause(Throwable cause) {
            this.cause = cause;
        }
    }
}

2. Observer (listener)

Similarly, if you want to realize the one to many relationship between the observed and the observer, you need to use the interface or abstract class to realize decoupling. Here, the interface is used, and the interface only defines the response method. then; Create the implementation class of the interface and implement the response method. In addition, the observer should add a method to create and start threads, and then register himself in Runnable to realize listening. The thread needs to call the notification method of Runnable (the response method of the observer is in the method body). In this way, as long as the observer has called the notification method, the observer will respond.

package threadobserver;

public interface LifecycleListener {
    void onEvent(ObserverableRunnable.RunnableEvent event);
}

package threadobserver;

import java.util.List;

/**
 * Life cycle observer
 */
public class LifecycleListenerObserver implements LifecycleListener{
    /**
     * 1,Implement onEvent method
     * 2,Provides methods for creating threads
     */

    private static final Object LOCK = new Object();

    public void concurrentQuery(List<String> ids) {
        ids.forEach(x -> {
            new Thread(new ObserverableRunnable(this) {
                @Override
                public void run() {
                    try {
                        notifyObserver(new RunnableEvent(RunnableState.RUNNING, Thread.currentThread(), null));
//                        Thread.sleep(10L);
                        notifyObserver(new RunnableEvent(RunnableState.DONE, Thread.currentThread(), null));
                    } catch (Throwable e) {
                        notifyObserver(new RunnableEvent(RunnableState.ERROR, Thread.currentThread(), e));
                    }
                }
            }, x).start();
        });
    }



    /**
     *
     * @param event
     */
    @Override
    public void onEvent(ObserverableRunnable.RunnableEvent event) {
        System.out.println("Current thread["+ event.getThread().getName() +"]" + "changed state and current state is ["+ event.getState() +"]");
        if (event.getCause() != null) {
            System.out.println("The runnable [" + event.getThread().getName() + "] process failed.");
            event.getCause().printStackTrace();
        }
    }
}

3. Client

package threadobserver;

import java.util.Arrays;

public class client {
    public static void main(String[] args) {
        new LifecycleListenerObserver().concurrentQuery(Arrays.asList("1", "2", "3", "4"));
    }
}

Topics: Java Multithreading