收藏一段经典的观察者访问者模式的代码

/*******************************************************************
 * This class replaces the Multicaster class that's described in
 * <i>Taming Java Threads</i>. It's better in almost every way
 * (It's smaller, simpler, faster, etc.). The primary difference
 * between this class and the original is that I've based
 * it on a linked-list, and I've used a Strategy object to
 * define how to notify listeners, thereby makeing the interface
 * much more flexible.
 * <p>
 * The <code>Publisher</code> class provides an efficient thread-safe means of
 * notifying listeners of an event. The list of listeners can be
 * modified while notifications are in progress, but all listeners
 * that are registered at the time the event occurs are notified (and
 * only those listeners are notified). The ideas in this class are taken
 * from the Java's AWTEventMulticaster class, but I use an (iterative)
 * linked-list structure rather than a (recursive) tree-based structure
 * for my implementation.
 * <p>
 * Here's an example of how you might use a <code>Publisher</code>:
 * <PRE>
 *	class EventGenerator
 *	{	interface Listener
 *		{	notify( String why );
 *		}
 *
 *		private Publisher publisher = new Publisher();
 *
 *		public void addEventListener( Listener l )
 *		{	publisher.subscribe(l);
 *		}
 *
 *		public void removeEventListener ( Listener l )
 *		{	publisher.cancelSubscription(l);
 *		}
 *
 *		public void someEventHasHappend(final String reason)
 *		{	publisher.publish
 *			(	
 *				// Pass the publisher a Distributor that knows
 *				// how to notify EventGenerator listeners. The
 *				// Distributor's deliverTo method is called
 *				// multiple times, and is passed each listener
 *				// in turn.
 *
 *				new Publisher.Distributor()
 *				{	public void deliverTo( Object subscriber )
 *					{	((Listener)subscriber).notify(reason);
 *					}
 *				}
 *			);
 *		}
 *	}
 * </PRE>
 * Since you're specifying what a notification looks like
 * by defining a Listener interface, and then also defining
 * the message passing symantics (inside the Distributor implementation),
 * you have complete control over what the notification interface looks like.
 *
 * @include /etc/license.txt
 */

public class Publisher
{
	public interface Distributor
	{	void deliverTo( Object subscriber );	// the Visitor pattern's
	}											// "visit" method.

	// The Node class is immutable. Once it's created, it can't
	// be modified. Immutable classes have the property that, in
	// a multithreaded system, access to the does not have to be
	// synchronized, because they're read only.
	//
	// This particular class is really a struct so I'm allowing direct
	// access to the fields. Since it's private, I can play
	// fast and loose with the encapsulation without significantly
	// impacting the maintainability of the code.

	private class Node
	{	public final Object subscriber;
		public final Node	next;

		private Node( Object subscriber, Node next )
		{	this.subscriber	= subscriber;
			this.next		= next;
		}

		public Node remove( Object target )
		{	if( target == subscriber )
				return next;

			if( next == null ) 						// target is not in list
				throw new java.util.NoSuchElementException
												(target.toString());

			return new Node(subscriber, next.remove(target));
		}

		public void accept( Distributor deliveryAgent ) // deliveryAgent is
		{	deliveryAgent.deliverTo( subscriber );		 // a "visitor"
		}
	}

	private volatile Node subscribers = null;

	/** Publish an event using the deliveryAgent. Note that this
	 *  method isn't synchronized (and doesn't have to be). Those
	 *  subscribers that are on the list at the time the publish
	 *  operation is initiated will be notified. (So, in theory,
	 *  it's possible for an object that cancels its subsciption
	 *  to nonetheless be notified.) There's no universally "good"
	 *  solution to this problem.
	 */

	public void publish( Distributor deliveryAgent )
	{	for(Node cursor = subscribers; cursor != null; cursor = cursor.next)
			cursor.accept( deliveryAgent );
	}

	synchronized public void subscribe( Object subscriber )
	{	subscribers = new Node( subscriber, subscribers );
	}

	synchronized public void cancelSubscription( Object subscriber )
	{	subscribers = subscribers.remove( subscriber );
	}

	//------------------------------------------------------------------
	private static class Test
	{
		static final StringBuffer actualResults   = new StringBuffer();
		static final StringBuffer expectedResults = new StringBuffer();

		interface Observer
		{	void notify( String arg );
		}

		static class Notifier
		{	private Publisher publisher = new Publisher();

			public void addObserver( Observer l )
			{	publisher.subscribe(l);
			}

			public void removeObserver ( Observer l )
			{	publisher.cancelSubscription(l);
			}

			public void fire( final String arg )
			{	publisher.publish
				(	new Publisher.Distributor()
					{	public void deliverTo( Object subscriber )
						{	((Observer)subscriber).notify(arg);
						}
					}
				);
			}
		}

		public static void main( String[] args )
		{
			Notifier source = new Notifier();
			int      errors = 0;

			Observer listener1 =
				new Observer()
				{	public void notify( String arg )
					{	actualResults.append( "1[" + arg + "]" );
					}
				};

			Observer listener2 =
				new Observer()
				{	public void notify( String arg )
					{	actualResults.append( "2[" + arg + "]" );
					}
				};

			source.addObserver( listener1 );
			source.addObserver( listener2 );

			source.fire("a");
			source.fire("b");

			expectedResults.append("2[a]");
			expectedResults.append("1[a]");
			expectedResults.append("2[b]");
			expectedResults.append("1[b]");

			source.removeObserver( listener1 );

			try
			{	source.removeObserver(listener1);
				System.err.print("Removed nonexistant node!");
				++errors;
			}
			catch( java.util.NoSuchElementException e )
			{	// should throw an exception, which we'll catch
				// (and ignore) here.
			}

			expectedResults.append("2[c]");
			source.fire("c");

			if( !expectedResults.toString().equals(actualResults.toString()) )
			{
				System.err.print("add/remove/fire failure.\n");
				System.err.print("Expected:[");
				System.err.print( expectedResults.toString() );
				System.err.print("]\nActual:  [");
				System.err.print( actualResults.toString() );
				System.err.print("]");
				++errors;
			}

			source.removeObserver( listener2 );
			source.fire("Hello World");
			try
			{	source.removeObserver( listener2 );
				System.err.println("Undetected illegal removal.");
				++errors;
			}
			catch( Exception e ) { /*everything's okay, do nothing*/ }

			if( errors == 0 )
				System.err.println("com.holub.tools.Publisher: OKAY");
			System.exit( errors );
		}
	}
}

 

 

 

Node 的设计考虑比较细致,订阅者不会接收到订阅前发布的主题。

 

 

 

你可能感兴趣的:(设计模式,thread,Access)