/** * A generic implementation of an Observable object as defined by the Observer pattern. * As a type parameter the interface for the Observer needs to be specified. * * @author Steven Jeuris * @param * The interface which should be implemented by the observers. */ public abstract class AbstractObservable implements IAbstractObservable { private T m_eventDispatcher = null; /** * The list of observers to which the event dispatcher forwards it calls. */ private final ObserverPool m_observers = new ObserverPool(); /** * Get the event dispatcher through which you can notify the observers. * * @return The event dispatcher through which you can notify the observers. */ protected T getEventDispatcher() { // Only create one instance of the dispatcher. if ( m_eventDispatcher == null ) { // Use reflection to get the generic parameter type. // Find the super class after 'AbstractObservable'. Class superClass = this.getClass(); while ( superClass.getSuperclass() != AbstractObservable.class ) { superClass = superClass.getSuperclass(); } // Get the generic class for AbstractObservable, so that parameter types // can be extracted. Type genericClass = superClass.getGenericSuperclass(); if ( genericClass instanceof Class ) { new RuntimeException( "Observable requires a parameter type!" ); } else { // Get the parameter type. ParameterizedType genericType = (ParameterizedType)genericClass; Type[] typeArguments = genericType.getActualTypeArguments(); m_eventDispatcher = m_observers.createEventDispatcher( (Class)typeArguments[0] ); } } return m_eventDispatcher; } /* * (non-Javadoc) * * @see be.hyp3.patterns.observer.IObservable#addObserver(T) */ public void addObserver( T observer ) { m_observers.addObserver( observer ); } /* * (non-Javadoc) * * @see be.hyp3.patterns.observer.IObservable#removeObserver(T) */ public boolean removeObserver( T observer ) { return m_observers.removeObserver( observer ); } } /** * The ObserverPool is a proxy which allows calls to an interface to be forwarded to a set of listeners. * * @author Steven Jeuris * * @param * The interface which defines which calls can be made to the listeners. */ class ObserverPool implements InvocationHandler { /** * The list of listeners. Additions and removals greatly outnumber traversals, * so a CopyOnWriteArrayList is the most efficient solution. */ private List m_pool = new CopyOnWriteArrayList(); /** * Add an observer to which the calls will be made. * * @param observer * The observer to add. */ public void addObserver( T observer ) { m_pool.add( observer ); } /** * Remove an observer to which calls where being made. * * @param observer * The observer to remove. * * @return True, when the observer was found and removed, false otherwise. */ public boolean removeObserver( T observer ) { return m_pool.remove( observer ); } /** * Create the proxy which allows to dispatch all calls to the observers. * * @param observerClass * The interface class of the observers. * * @return The dispatcher which can be used to make calls to all added observers. */ @SuppressWarnings( "unchecked" ) public T createEventDispatcher( Class observerClass ) { T dispatcher = (T)Proxy.newProxyInstance( observerClass.getClassLoader(), new Class[] { observerClass }, this ); return dispatcher; } /** * invoke() implementation of InvocationHandler. * This is called whenever a call is made to an event dispatcher. */ @Override public Object invoke( Object object, Method method, Object[] args ) throws Throwable { // Forward the call to all observers. for ( T observer : m_pool ) { method.invoke( observer, args ); } // No return object available. return null; } } /** * An interface to the abstract Observable class. * * @author Steven Jeuris * @param */ public interface IAbstractObservable { /** * Add an observer which will listen to the actions of this object. * * @param observer * The observer which should listen to this observable. */ public abstract void addObserver( T observer ); /** * Remove an observer which was listening to this object. * * @param observer * The observer to remove. * * @return True, when observer was found and removed, false otherwise. */ public abstract boolean removeObserver( T observer ); }