`
okhaoba
  • 浏览: 5442 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

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

阅读更多
/*******************************************************************
 * 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 的设计考虑比较细致,订阅者不会接收到订阅前发布的主题。

 

 

 

分享到:
评论

相关推荐

    设计模式:可复用面向对象软件的基础--详细书签版

    5.7 observer(观察者)—对象行为型 模式 194 5.8 state(状态)—对象行为型模式 201 5.9 strategy(策略)—对象行为型 模式 208 5.10 template method(模板方法) —类行为型模式 214 5.11 visitor...

    二十三种设计模式【PDF版】

    设计模式之 Observer(观察者) 介绍如何使用 Java API 提供的现成 Observer 设计模式之 Iterator(迭代器) 这个模式已经被整合入Java的Collection.在大多数场合下无需自己制造一个Iterator,只要将对象装入...

    asp.net知识库

    经典正则表达式 delegate vs. event 我是谁?[C#] 表达式计算引擎 正式发布表达式计算引擎WfcExp V0.9(附源码) 运算表达式类的原理及其实现 #实现的18位身份证格式验证算法 身份证15To18 的算法(C#) 一组 正则表达式...

    UML和模式应用(架构师必备).part02.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    UML和模式应用(架构师必备).part06.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    UML和模式应用(架构师必备).part01.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    UML和模式应用(架构师必备).part07.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    UML和模式应用(架构师必备).part03.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    UML和模式应用(架构师必备).part04.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    UML和模式应用(架构师必备).part05.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    UML和模式应用(架构师必备).part08.rar

    26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...

    超级有影响力霸气的Java面试题大全文档

     栈是一种线形集合,其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。 堆是栈的一个组成元素 22、forward 和redirect的区别  forward是服务器请求资源,服务器直接访问目标地址的URL,把...

    ChatGPT访问错误说明

    ChatGPT是美国人工智能研究实验室OpenAI新推出的一种人工智能技术驱动的自然语言处理工具,使用了Transformer神经网络架构,也是GPT-3.5架构,这...此外,ChatGPT盈利模式尚处于探索阶段,后续商业化落地进展有待观察。

    java 面试题 总结

    栈是一种线形集合,其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。 堆是栈的一个组成元素 19、forward 和redirect的区别 forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL...

    java面试800题

    template模板方法模式、memento备忘录模式、observer观察者模式、command命令模式、state状态模式、strategy策略模式、mediator调停者模式、interpreter解释器模式、visitor访问者模式、chain of responsibility责任...

    软件设计规范

    原则是,每段代码,每处需要理解的地方,如果和总体架构相关,就要有说明。 软件领域需要简化。需要还原软件本来的面目。EDA有泛滥的趋势,软件的各个方面都需要简化。软件形态、需求分析、文档说明、开发工具等。 ...

    PLSQLDeveloper下载

    在本文第一段代码中有一句话如下: cursor c_emp is select * from employee where emp_id=3; 其含义是定义一个游标c_emp,代表employee表中所有emp_id字段为3的结果集。当需要操作该结果集时,必须完成三步:打开...

    Linux高性能服务器编程

    8.5.1 半同步半异步模式 8.5.2 领导者追随者模式 8.6 有限状态机 8.7 提高服务器性能的其他建议 8.7.1 池 8.7.2 数据复制 8.7.3 上下文切换和锁 第9章 IO复用 9.1 select系统调用 9.1.1 select API 9.1.2...

Global site tag (gtag.js) - Google Analytics