Java design pattern 2: observer pattern

Keywords: Java Spring less

Observer mode

Definition

One to many dependencies between objects are established, so that when the state of an object changes, all its dependents will be notified and updated automatically.

In observer mode, when the state of the observed object changes, all observers will be notified. There are two modes of PUSH and PULL. PUSH means that the observed object actively updates the content to the observer, while PULL means that the observer actively obtains the updated content from the observed object.

The observer pattern often appears in Java GUI, Web, Spring and other places.

UML class diagram

In fact, the java.util package of Java already contains some of the most basic things of Observer pattern design: the Observable class and the Observer interface. But the Observable class and the Observer interface are annotated as Deprecated after Java 9.

The Observable class can be regarded as a kind of "Observable" thing, in which changed is a boolean type member variable indicating whether the thing has changed. In obs, a Vector list consisting of observers is maintained. The setChanged() method and the notifyObservers() method play an important role.

public void notifyObservers(Object arg) {

     //Array of temporary objects, storing observers
    Object[] arrLocal;

    synchronized (this) {
        if (!changed)
            return;
        arrLocal = obs.toArray();
        //Set changed to false
        clearChanged();
    }

    //Reverse polling notifies observers
    for (int i = arrLocal.length-1; i>=0; i--)
        ((Observer)arrLocal[i]).update(this, arg);
}

protected synchronized void setChanged() {
    changed = true;
}

Only when the changed member variable is true can status updates be notified to all observers. So you need to call the setChanged() method every time you notify the observer to update the status.

The Observer interface is used to observe "observable" things, and the update method is used to update the state. The first parameter is the observed object, and the second parameter is the information passed by the observed object.

Demonstration

publisher and subscriber are used to design a simple implementation of observer pattern using components provided by Java.

In PUSH mode

Publisher.class

public class Publisher extends Observable {
    private ArrayList<String> books;

    Publisher(){
        books = new ArrayList<>();
    }

    public void addBook(String book){
        books.add(book);
        super.setChanged();
        super.notifyObservers(books);
    }
}

Subscriber.class

public class Subscriber implements Observer {

    private String name;

    Subscriber(String name){
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        if(arg instanceof ArrayList){
            ArrayList<String> books = (ArrayList<String>)arg;
            System.out.print(name+" ");
            books.stream().forEach(book -> System.out.print(book+" "));
            System.out.println();
        }
    }
}

Main.class

public class Main {
    public static void main(String[] args) {
        Publisher publisher = new Publisher();

        publisher.addObserver(new Subscriber("A"));
        publisher.addObserver(new Subscriber("B"));
        publisher.addObserver(new Subscriber("C"));

        System.out.println("Publisher A new book!");
        publisher.addBook("data structure");

        System.out.println("Publisher A new book!");
        publisher.addBook("operating system");

        System.out.println("Publisher A new book!");
        publisher.addBook("Analog circuit");

    }
}
Publisher has a new book!
C data structure
 B data structure
 A data structure
 Publisher has a new book!
C data structure operating system
 B data structure operating system
 A data structure operating system
 Publisher has a new book!
C data structure operating system analog circuit
 B data structure operating system analog circuit
 A data structure operating system analog circuit

In PULL mode

Publisher.class

public class Publisher extends Observable {

    //... code unchanged, change the following

    public void addBook(String book){
        books.add(book);
        super.setChanged();
        super.notifyObservers();
    }

    public ArrayList<String> getBooks() {
        return books;
    }
}

Subscriber.class

public class Subscriber implements Observer {

    //... code unchanged, change the following

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof Publisher){
            Publisher publisher = (Publisher)o;

            System.out.print(name+" ");
            publisher.getBooks().stream().forEach(book -> System.out.print(book+" "));
            System.out.println();
        }
    }
}

Main.class

Publisher has a new book!
C data structure
 B data structure
 A data structure
 Publisher has a new book!
C data structure operating system
 B data structure operating system
 A data structure operating system
 Publisher has a new book!
C data structure operating system analog circuit
 B data structure operating system analog circuit
 A data structure operating system analog circuit

shortcoming

  1. The Java provided Observable class polls subscribers in a fixed order, subscribing first and then receiving. In this way, the coupling degree between them is increased. After all, the purpose of designing this pattern is to reduce the coupling degree between interactive objects.
for (int i = arrLocal.length-1; i>=0; i--)
    ((Observer)arrLocal[i]).update(this, arg);
  1. Observable is a class, not an interface. Because Java does not inherit much, it limits the extensibility of publishers and weakens the reusability of code.

  2. The observable class defines the two most critical methods as protected, which means that you can only inherit the observable class, otherwise you cannot call the two methods and implement the observer pattern. In this way, it is impossible to combine the observable class with other classes, which violates the design principle of "multi-purpose combination, less inheritance".

protected synchronized void setChanged() {
        changed = true;
}
protected synchronized void clearChanged() {
    changed = false;
}
Published 7 original articles, won praise 0, visited 32
Private letter follow

Posted by Ellen67 on Thu, 16 Jan 2020 20:58:22 -0800