Design-Pattern -- Observer Pattern

1. Definition:

  Let us first look at the strict definition of Observer Pattern,

 **Observer Pattern defines a One-to-many dependencies between objects. Once the Object change state, all of their dependencies are notified and changed automatically.**

  There are several terminology used in the Observer Pattern , which are as follows:

  • Observable : This object has some attributes which may change frequently. It is also known as Subject.

  • Observer: The object which has to receive those frequently modified attributes from the Observable objects. They are also known as Object.

  • One-to-many dependencies: There is only one Observable objects and many Observers related to it. This means that the Observable object has a collection of Observers who subscribe it.

The following figure shows their relationship.

Smiley face
1 - n relationship

Observer Pattern usually used in the scenario that one object are active to have some properties changed quite often while at the same time, some objects wants to know some information from that object as soon as possible. For example, one temperature sensor versus some distributed diplays who show the temperature in differnt way, one writer who post articles versus several readers who subscribe him or her.

2. UML:

 Now, let us see the general UML diagram for Observer pattern.

Smiley face
UML diagram

For observable interface it defines three behaviours of observable object.

  1. add method by passing an object who implements observer interface;
  2. delete method by passing an object who implements observer interface;
  3. nofityAll method: it iterates all observers and call the update method defined in each observer.

For the Concrete Observable who implements the interface, it need to have two properties, which are

  1. State state : the frequently changed state
  2. A Collection of Observers who want to get the state from the Observable, it can be either list, set or dictionary depending on different application for insertion, deletion and iteration of observers.

Two important things to be understand about Observer Pattern.

  1. The constructer of Concrete Observer should have one argument which is the reference of the observable it want to subscribe. Not only Observable should have a set of observers, but also each observer should know the follower so that they can invoke the getState() method to update the state.

  2. The arguments type in constructors and methods should be in interface type rather than entities cos the interface design principle and least knowledge design principle.We could casting them to entity type if necessary in some methods.

3. Example and Implementation in Java:

Now, let us see an Example and implement it by using java.

Example:

  There is a Publishing House who can publish new books.
  Meanwhile, here are readers who can follow and unfollow this publishing house.
  Once the publish house publish a new book, it will broadcast notification to all its followers.
  Write program to mimic the process.

First, implement two interfaces.
IObservable:

public interface IObservable {
    void  addObserver(IObserver observer);
    void  removeObserver(IObserver observer);
    void  notifyAllObservers();
}

IObserver:

public interface IObserver {
    void update();
}

Then, implements Publisher and Reader as concrete implementation classes.
Observable: Publisher

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Publisher implements IObservable {

    private String publisherName;
    private Set<IObserver> readers; // set of observers which are in type of Reader class
    private List<String> books ; // list of new published book

    //no-args constructors
    public Publisher() {
        super();
        readers = new HashSet<>();
        books =  new ArrayList<>();
    }

    public Publisher(String publisherName){
        this.publisherName = publisherName;
        readers = new HashSet<>();
        books =  new ArrayList<>();

    }

    public void publishNewBook(String bookName){
        if(bookName == null) return;
        books.add(bookName);
        this.notifyAllObservers();

    }
    @Override
    public void addObserver(IObserver observer) {
        // if the observer is null or if it is not an instance of Reader, return;
        if (observer == null || !(observer instanceof Reader)) return;
        // if it already subscribed the publisher
        if (readers.contains(observer)) return;
        readers.add(observer);

        Reader newReader = (Reader)observer; // cast to Reader object
        System.out.println(newReader.getReaderName()+" has subscribed " + this.getPublisherName());
    }

    @Override
    public void removeObserver(IObserver observer) {
        if(readers.contains(observer)){
            readers.remove(observer);
            Reader reader = (Reader)observer;
            System.out.println(reader.getReaderName()+" has unfollowed " + this.getPublisherName());
        }
    }

    @Override
    public synchronized void notifyAllObservers() {
        // iterate all observers and notify them.
        for(IObserver reader: readers){
            reader.update();
        }
    }


    // Getters
    public String getLatestPublishedBookName(){
        return books.get(books.size()-1);
    }

    public String getPublisherName() {
        return publisherName;
    }

    public void setPublisherName(String publisherName) {
        this.publisherName = publisherName;
    }
}

Observer: Reader

public class Reader implements IObserver {

    private String readerName;
    private Publisher publisher;

    public Reader(String name, Publisher publisher){
        this.readerName = name;
        this.publisher = publisher;//dependencies injection
    }


    @Override
    public void update() {
        System.out.println(this.readerName + " Knows that the publisher published a new book, called " + this.publisher.getLatestPublishedBookName());
    }

    public String getReaderName() {
        return readerName;
    }
}

Finally, write the main function to mimic the process.

public class ObserverPatternTest {
    public static void main(String[] args) throws InterruptedException {
        Publisher  publisher = new Publisher("BOOK STORE 1");
        Reader tom = new Reader("Tom",publisher);
        Reader ben = new Reader("Ben",publisher);
        Reader alice = new Reader("Alice",publisher);

        publisher.addObserver(tom);
        publisher.addObserver(ben);
        publisher.addObserver(alice);


        publisher.publishNewBook("Introduction to Design Pattern ");
        System.out.println("A few months later");

        Thread.currentThread().sleep(1000);
        Reader mike = new Reader("Mike",publisher);
        publisher.addObserver(mike); // new subscriber
        publisher.removeObserver(tom);
        Thread.currentThread().sleep(1000);

        publisher.publishNewBook("Algorithms and Data Structure");
    }
}

Output:

Tom has subscribed BOOK STORE 1
Ben has subscribed BOOK STORE 1
Alice has subscribed BOOK STORE 1

Ben Knows that the publisher published a new book, called Introduction to Design Pattern
Tom Knows that the publisher published a new book, called Introduction to Design Pattern
Alice Knows that the publisher published a new book, called Introduction to Design Pattern

A few months later
Mike has subscribed BOOK STORE 1
Tom has unfollowed BOOK STORE 1

Ben Knows that the publisher published a new book, called Algorithms and Data Structure
Mike Knows that the publisher published a new book, called Algorithms and Data Structure
Alice Knows that the publisher published a new book, called Algorithms and Data Structure

We can view the automatically generated UML diagram by IntelliJ IDEA.

Smiley face
UML diagram by IntelliJ

Author: Liang Tan
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Liang Tan !
  TOC