简体   繁体   中英

Normal observer pattern vs template observer pattern

I want to know if the observer pattern configered with a template observer is more convenient than the general observer pattern. The concept of a template observer is a observable class which has a specific observer type configered as a template. As an example I will show a normal observer and compare it with a template observer.

Normal observer pattern

Normal observer interface:

   public interface NormalObserver {

    //Update method
    void update(final String updateIn);
}

The update function is the same for every class that implements the NormalObserver interface. This causes problems when you want to implement a specific observer, for example an observer with different update parameters.

Concrete observer:

 public class ConcreteNormalObserver implements NormalObserver {

    //Update implication for NormaObserver interface
    @Override
    public void update(String updateIn) {
        System.out.println(updateIn);
    }
}

Normal observable interface:

 public interface NormalObservable {

    //method for adding an observer
    void addObserver(NormalObserver observerIn);

    //method for removing an observer
    void removeObserver(NormalObserver observerIn);

    //method for notifying observers
    void notifyObservers();

}

Concrete normal observable:

    public class ConcreteNormalObservable implements NormalObservable {

    //List for Observer
    private List<NormalObserver> mObservers;

    public ConcreteNormalObservable() {
        this.mObservers = new ArrayList<>();
    }

    @Override
    public void addObserver(NormalObserver observerIn) {

        if(observerIn != null && !this.mObservers.contains(observerIn)) {
            this.mObservers.add(observerIn);
        }
    }

    @Override
    public void removeObserver(NormalObserver observerIn) {
        this.mObservers.remove(observerIn);
    }

    @Override
    public void notifyObservers() {

        for(NormalObserver lObserver: this.mObservers) {
            lObserver.update("Update");
        }
    }
}

This is in my opinion a straightforward observer pattern, you could argue that the notifyObservers method is a bit redundant because an observable could just call the update method from its observers anywhere.

Template observer pattern

Template observable interface:

  /**
 * @param <ObserverType> Concrete observer to implement in observable,
 * this ObserverType specifies the update functions which the TemplateObservable can call.
 */
public interface TemplateObservable<ObserverType> {

    /**
     * Function to add an Observer of a specific type
     * @param observerIn
     */
    void addObserver(ObserverType observerIn);

    /**
     * Function to remove an Observer of a specific type
     * @param observerIn
     */
    void removeObserver(ObserverType observerIn);

}

The template type defines which observer type you are going to use, so a concrete observable specifies which observer it is going to use.

Concrete observable template:

   /**
 * Concrete example of a class that implements TemplateObservable.
 * This class is uses a ConcreteTemplateObserver as its observerType.n
 */
public class ConcreteTemplateObservable implements TemplateObservable<ConcreteTemplateObserver> {

    //List to hold all the observers
    private List<ConcreteTemplateObserver> mObservers;

    public ConcreteTemplateObservable() {
        this.mObservers = new ArrayList<>();
    }

    @Override
    public void addObserver(ConcreteTemplateObserver observerIn) {

        //Check for non null and if the observer parameter is not already in the observerList
        if(observerIn != null && !this.mObservers.contains(observerIn)) {
            this.mObservers.add(observerIn);
        }
    }

    @Override
    public void removeObserver(ConcreteTemplateObserver observerIn) {
        this.mObservers.remove(observerIn);
    }

    //Own update function, no need for notify function, but can be optional.
    public void update() {

        for(ConcreteTemplateObserver lObserver: this.mObservers) {
            lObserver.updateTemplateObserver("Update");
        }
    }
}

Concrete template observer:

  public class ConcreteTemplateObserver {

    public void updateTemplateObserver(final String messageIn) {
        System.out.println(messageIn);
    }
}

I am convinced that the template observer pattern is more usefull because of the specific update functions you can call from the observable whereas in the normal observable you are forced to use the normal update function, but I am doubtfull if the template observable meets the criteria of an observer pattern.

I see very little advantage one way or the other. In practice, an observable object can send several different kinds of notifications, and can also accept several types of observers.

Here is another model that implements once and for all the observer list manipulation, as well as the dispatch mechanism.

Consider the Observable class:

public abstract class Observable {
    private static final Logger LOG
            = Logger.getLogger(Observable.class.getName());
    private final List observers = new ArrayList<>();

    protected void addObserver(Object observer) {
        observers.add(observer);
    }

    protected void removeObserver(Object listener) {
        observers.remove(listener);
    }

    protected <T> T getNotifier(Class<T> intf) {
        ClassLoader cl = intf.getClassLoader();
        return intf.cast(Proxy.newProxyInstance(cl, new Class[] {intf},
                new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                return dispatch(method, args);
            }
        }));
    }

    protected Object dispatch(Method method, Object args[]) {
        Class<?> intf = method.getDeclaringClass();
        Object result = null;
        for (Object observer: observers) {
            try {
                if (intf.isInstance(observer)) {
                    result = method.invoke(observer, args);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                LOG.log(Level.SEVERE, "Error invoking listener method", e);
            }
        }
        return result;
    }
}

Now as an example of how to use it, take an observable list, that accepts observers implementing the following interface:

public interface ListObserver<T> {
    public void elementAdded(int i, T newElem);
    public void elementRemoved(int i, T oldElem);
    public void elementReplaced(int i, T oldElem, T newElem);
}

The observable list would be implemented as follow:

public class ObservableList<T> extends Observable {
    private final List<T> elements = new ArrayList<>();

    private final ListObserver<T> notifier = getNotifier(ListObserver.class);

    public void addObserver(ListObserver<T> obs) {
        super.addObserver(obs);
    }

    public void removeObserver(ListObserver<T> obs) {
        super.removeObserver(obs);
    }

    public void addElement(T newElem) {
        int i = elements.size();
        elements.add(newElem);
        notifier.elementAdded(i, newElem);
    }

    public void removeElement(int i) {
        T oldElem = elements.remove(i);
        notifier.elementRemoved(i, oldElem);
    }

    public void setElement(int i, T newElem) {
        T oldElem = elements.set(i, newElem);
        notifier.elementReplaced(i, oldElem, newElem);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM