Skip to main content
corrected spelling.
Source Link
h.j.k.
  • 19.2k
  • 3
  • 36
  • 93

A Generic Observable is a neat idea. Much of the code that was initially designed for the ObeserverObserver pattern in Java was then actually built using a custom interface for each 'event', and you ended up, for example, with the Listener and Event interfaces that are so common in the AWT/Swing frameworks. A more general-purpose system has merits though.

Further, you have synchronizatonsynchronization built in to it, but the synchronization is on the instance, which is unsafe (what if someone synchronized on your instance for some other reason, suddenly your locking strategy is confounded).

Additionally, the synchroizationsynchronization uses an internal instance, and I use Java8 suppierssuppliers to have a constructor giving a collection. That way you can get a safe instance for the synchronization.

The original Observer is not synchronized, and, this one does things a little differently in the notify method, because it does the notification after doing the changed=false change. THeThe JavaDoc for the original Observable indicates that the change should only be reset after the notifications....

A Generic Observable is a neat idea. Much of the code that was initially designed for the Obeserver pattern in Java was then actually built using a custom interface for each 'event', and you ended up, for example, with the Listener and Event interfaces that are so common in the AWT/Swing frameworks. A more general-purpose system has merits though.

Further, you have synchronizaton built in to it, but the synchronization is on the instance, which is unsafe (what if someone synchronized on your instance for some other reason, suddenly your locking strategy is confounded).

Additionally, the synchroization uses an internal instance, and I use Java8 suppiers to have a constructor giving a collection. That way you can get a safe instance for the synchronization.

The original Observer is not synchronized, and, this one does things a little differently in the notify method, because it does the notification after doing the changed=false change. THe JavaDoc for the original Observable indicates that the change should only be reset after the notifications....

A Generic Observable is a neat idea. Much of the code that was initially designed for the Observer pattern in Java was then actually built using a custom interface for each 'event', and you ended up, for example, with the Listener and Event interfaces that are so common in the AWT/Swing frameworks. A more general-purpose system has merits though.

Further, you have synchronization built in to it, but the synchronization is on the instance, which is unsafe (what if someone synchronized on your instance for some other reason, suddenly your locking strategy is confounded).

Additionally, the synchronization uses an internal instance, and I use Java8 suppliers to have a constructor giving a collection. That way you can get a safe instance for the synchronization.

The original Observer is not synchronized, and, this one does things a little differently in the notify method, because it does the notification after doing the changed=false change. The JavaDoc for the original Observable indicates that the change should only be reset after the notifications....

Add example code
Source Link
rolfl
  • 97.5k
  • 17
  • 218
  • 418

If it were me, I would keep the generics a bit simpler, and would implement the code much closer to the original Observer, except adding the generics.

I have used your code as a template, but then restructured, and rewritten parts, to be:

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;

/**
 * like java.util.Observable, But uses generics to avoid need for a cast.
 *
 * For any un-documented variable, parameter or method, see java.util.Observable
 */
public class Observable<T> {

    public interface Observer<U> {
        public void update(Observable<? extends U> observer, U arg);
    }

    private boolean changed = false;
    private final Collection<Observer<? super T>> observers;

    public Observable() {
        this(ArrayList::new);
    }
    
    public Observable(Supplier<Collection<Observer<? super T>>> supplier) {
        observers = supplier.get();
    }

    public void addObserver(final Observer<? super T> observer) {
        synchronized (observers) {
            if (!observers.contains(observer)) {
                observers.add(observer);
            }
        }
    }

    public void removeObserver(final Observer<? super T> observer) {
        synchronized (observers) {
            observers.remove(observer);
        }
    }

    public void clearObservers() {
        synchronized (observers) {
            this.observers.clear();
        }
    }

    public void setChanged() {
        synchronized (observers) {
            this.changed = true;
        }
    }

    public void clearChanged() {
        synchronized (observers) {
            this.changed = false;
        }
    }

    public boolean hasChanged() {
        synchronized (observers) {
            return this.changed;
        }
    }
    
    public int countObservers() {
        synchronized (observers) {
            return observers.size();
        }
    }
    
    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(final T value) {
        ArrayList<Observer<? super T>> toNotify = null;
        synchronized(observers) {
            if (!changed) {
                return;
            }
            toNotify = new ArrayList<>(observers);
            changed = false;
        }
        for (Observer<? super T> observer : toNotify) {
            observer.update(this, value);
        }
    }
}

Note that, apart from the generics, the signatures are the same as the existing Observable.

Additionally, the synchroization uses an internal instance, and I use Java8 suppiers to have a constructor giving a collection. That way you can get a safe instance for the synchronization.

The original Observer is not synchronized, and, this one does things a little differently in the notify method, because it does the notification after doing the changed=false change. THe JavaDoc for the original Observable indicates that the change should only be reset after the notifications....

If it were me, I would keep the generics a bit simpler, and would implement the code much closer to the original Observer, except adding the generics.

I have used your code as a template, but then restructured, and rewritten parts, to be:

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;

/**
 * like java.util.Observable, But uses generics to avoid need for a cast.
 *
 * For any un-documented variable, parameter or method, see java.util.Observable
 */
public class Observable<T> {

    public interface Observer<U> {
        public void update(Observable<? extends U> observer, U arg);
    }

    private boolean changed = false;
    private final Collection<Observer<? super T>> observers;

    public Observable() {
        this(ArrayList::new);
    }
    
    public Observable(Supplier<Collection<Observer<? super T>>> supplier) {
        observers = supplier.get();
    }

    public void addObserver(final Observer<? super T> observer) {
        synchronized (observers) {
            if (!observers.contains(observer)) {
                observers.add(observer);
            }
        }
    }

    public void removeObserver(final Observer<? super T> observer) {
        synchronized (observers) {
            observers.remove(observer);
        }
    }

    public void clearObservers() {
        synchronized (observers) {
            this.observers.clear();
        }
    }

    public void setChanged() {
        synchronized (observers) {
            this.changed = true;
        }
    }

    public void clearChanged() {
        synchronized (observers) {
            this.changed = false;
        }
    }

    public boolean hasChanged() {
        synchronized (observers) {
            return this.changed;
        }
    }
    
    public int countObservers() {
        synchronized (observers) {
            return observers.size();
        }
    }
    
    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(final T value) {
        ArrayList<Observer<? super T>> toNotify = null;
        synchronized(observers) {
            if (!changed) {
                return;
            }
            toNotify = new ArrayList<>(observers);
            changed = false;
        }
        for (Observer<? super T> observer : toNotify) {
            observer.update(this, value);
        }
    }
}

Note that, apart from the generics, the signatures are the same as the existing Observable.

Additionally, the synchroization uses an internal instance, and I use Java8 suppiers to have a constructor giving a collection. That way you can get a safe instance for the synchronization.

The original Observer is not synchronized, and, this one does things a little differently in the notify method, because it does the notification after doing the changed=false change. THe JavaDoc for the original Observable indicates that the change should only be reset after the notifications....

Source Link
rolfl
  • 97.5k
  • 17
  • 218
  • 418

A Generic Observable is a neat idea. Much of the code that was initially designed for the Obeserver pattern in Java was then actually built using a custom interface for each 'event', and you ended up, for example, with the Listener and Event interfaces that are so common in the AWT/Swing frameworks. A more general-purpose system has merits though.

What will be the usage pattern for it, though? Is there ever a need to create subclasses? Is there a need for the factory-methods you have created? Note, those factory methods cannot ever create instances of a sub-class.

Further, you have synchronizaton built in to it, but the synchronization is on the instance, which is unsafe (what if someone synchronized on your instance for some other reason, suddenly your locking strategy is confounded).

Finally, there's a reason that the Observer instance in the JDK has a two-argument notify, and that's because it needs to know where the notification came from. That way the Observer can observe multiple Observables, and know which one the notification originates from.

These are 4 significant issues I see in your design.

Your implementation is a bit shaky too:

  • the indentation is a mess
  • braced 1-liners
  • poor variable names
  • duplicate methods (deleteObserver calls rm, addObserver calls add, deleteObservers calls clear)
  • unimplemented methods (getInstance())

All in all, there's a lot to consider, and this is not done the way I would expect.