0
\$\begingroup\$

About the Program

It's a simple implementation of Observer Pattern.

The choice of different NotifyMethod will affect how those observers are notified. The SimpleNotify in blow just uses a simple for loop.

The concrete Observer is initialized with a concrete Observable that it want to observe. In the blow program concrete Observer = Reader, and concrete Observable = Bookstore.

Some Problems I've Noticed in the Program

  1. A Bookstore instance is initialized with a NotifyMethod instance, however to enable dynamic changing of NotifyMethod one have to "move" those readers from old method to new method.

  2. To make a class Observable, one has to modify the class (constructor, Observable's contracts), is it possible to make a Bookstore instance unaware of that it's being observed? i.e. How to design a class which can be changed from observable to not observable at runtime? Or this isn't an issue?

  3. The Observer interface is update() without any arguments, so when implementing concrete Observer it has to keep a reference to a concrete Observable(Bookstore in this case), and access the Bookstore's data by public interface. Is this a good idea? What's the drawback of this?

The Feature I want

From the current Observer interface, an concrete Observer can't observer multiple observables:

Person implements 
    BookstoreObserver, StockmarketObserver, ... {

    bookstoreUpdate() { ... }
    stockmarketUpdate() { ... }
    ...

}

If there are multiple Observer interface then the update logic of each concrete Observable won't be lumped into the only one update() if only one Observer interface. Is this a good idea?

Finally, is it a good idea to make Reader delegate its job to observe the Bookstore instance to an Observer? i.e. The Reader also use composition like Bookstore in the below program to achieve its Observer effect?


The UML

Sorry for not showing much details, I used BlueJ and it can't show the interface/public methods of classes, but the code below is rather simple.

UML of Single Observable - Observer Pattern

A typical Observer Pattern for reference. Standard Observer Pattern

Code

public interface Observable
{
    public void register(Observer o);
    public void unregister(Observer o);
    public void sendNews();
}
public interface NotifyMethod extends Observable
{}
/**
 * Use a simple for-each loop to send news to observers.
 */
public class SimpleNotify implements NotifyMethod
{
    private List<Observer> observers = new ArrayList<Observer>();

    public void register(Observer o) {
        observers.add(o);
    }
    public void unregister(Observer o) {
        observers.remove(o);
    }
    public void sendNews() {
        for (Observer observer: observers) {
            observer.update();
        }
    }
}
/**
 * An observable bookstore.
 */
public class Bookstore implements Observable
{
    private NotifyMethod notificationMethod;
    private Book newBook;
    //

    public Bookstore(NotifyMethod notificationMethod) {
        this.notificationMethod = notificationMethod;
    }

    public void publishNewBook(Book newBook) {
        this.newBook = newBook;
        sendNews();
    }

    public Book getNewBook() {
        return newBook;
    }

    //

    public void register(Observer o) {
        notificationMethod.register(o);
    }
    public void unregister(Observer o) {
        notificationMethod.unregister(o);
    }
    public void sendNews() {
        notificationMethod.sendNews();
    }

}
/**
 * Book = "Title - Author"
 */
public class Book
{
    private String title;
    private String author;

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    public String getTitle() {
        return title;
    }

    public String getAuthor() {
        return author;
    }
}
public interface Observer
{
    public void update();
}
/**
 * A Reader wants to observe the bookstore's news.
 */
public class Reader implements Observer
{
    private String name;
    private Bookstore bookstore;
    //
    public Reader(String name, Bookstore bookstore) {
        this.name = name;
        this.bookstore = bookstore;
        bookstore.register(this);
    }

    public void update() {
        System.out.format("Reader %s receives a book news \"%s - %s\"%n",
            name,
            bookstore.getNewBook().getTitle(),
            bookstore.getNewBook().getAuthor());
    }
}

Main Program and Output

public class Main
{
    public static void main(String[] args) {

        Bookstore myBookstore = new Bookstore(new SimpleNotify());
        Reader MrA = new Reader("Mr.A", myBookstore);
        Reader MrB = new Reader("Mr.B", myBookstore);

        myBookstore.publishNewBook(new Book("Alice in Wonderland", "Lewis Carroll"));
    }
}
Reader Mr.A receives a book news "Alice in Wonderland - Lewis Carroll"
Reader Mr.B receives a book news "Alice in Wonderland - Lewis Carroll"
\$\endgroup\$
6
  • \$\begingroup\$ are you aware of existence of java-rx? This is already implemented \$\endgroup\$ Apr 3, 2019 at 10:12
  • \$\begingroup\$ @KrzysztofSkowronek: So my questions are impossible to be solved without the library? \$\endgroup\$
    – RainningTW
    Apr 3, 2019 at 10:17
  • \$\begingroup\$ of course they are, but after answering them you will have next ones, and next ones, and they are all already implemented. Rx has multiple observers, broadcast, timing synchronization, buffering, windowing, thread marshaling etc. It's like "I want 10 objects in one variable, how can I improve this Array class?" -> use ArrayList from Java. There is no need for reinventing the wheel, especially that Rx is far more powerfull: you have built in also filtering, mapping notifications, sorting, counting, timers etc. Stand on the shoulders of giants instead of digging a hole :) \$\endgroup\$ Apr 3, 2019 at 10:23
  • 2
    \$\begingroup\$ @KrzysztofSkowronek: I understand what you want to convey, but I'm just interested about the mechanism behind it, I'm learning about observer pattern and I have many questions about it. If you think my question is inappropriate here I can delete it later, but I thought I can learn from the answer(s) to my questions. \$\endgroup\$
    – RainningTW
    Apr 3, 2019 at 10:26
  • \$\begingroup\$ @KrzysztofSkowronek: Thank you very much \$\endgroup\$
    – RainningTW
    Apr 3, 2019 at 11:03

1 Answer 1

1
\$\begingroup\$

As I said in comments, take a look at Java-Rx, or any Rx implementation, they are almost the same in almost all languages.

First of all, both IObservable and IObserver should be generic, where the type is type of the message in notification:

public interface Observable<T>
{
    public void register(Observer<T> o);
    public void unregister(Observer<T> o);
    public void sendNews(T msg);
}

public interface Observer<T>
{
    public void nextNews(T msg);
    public void finished(); // called when Observable knows it will not produce anymore
    public void error(Exception ex); // for error handling
}

Now, when observer is registered, observable can send to it full message, so it does not need a reference back to observable. Observable only stores a list of registered observers and when it emits something, it does:

// registration
Observer<Book> r = new Reader();
bookstore.register(r); // this adds r to the bookstore._registeredObservers

void sendNews(T msg)
{
   foreach(T i in _registeredObservers) // _registeredObservers : ArrayList<IObserver<T>>
      i.nextNews(msg);
}

In your example Bookstore could be Observable<BookOperation>, and book operation could be:

// pseudo code, I don't really know Java

    class BookOperation
{
   public Book book;
   public Operation operation; // this is enum: New, Removed, etc
}

Or, Bookstore could be just a class with members:

Bookstore.BookAdded : Observable<Book>
Bookstore.BookRemoved : Observable<Book>

In Rx in most cases you don't really implement Observers, you use register overload that takes lambas.

Real Observers are mostly implemented as operators: observers that are also observables.

For example, observable.filter(x => x > 5) will return another observable, but only with elements from the first one that are greater than 5.

This approach would also let you do:

Reader r = new Reader();

obs1.register(r);
obs2.register(r); // one reader observes two obervables

Also, when observer is done, it should uregister - this is managed via IDisposable interface in C#, I'm pretty sure that there is something similar in Java.

This answers all 3 of your questions, I hope.

Now, my coffee is empty, so if you have further questions, I will try to answer them later :)

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.