/*******************************************************************
* 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...
设计模式之 Observer(观察者) 介绍如何使用 Java API 提供的现成 Observer 设计模式之 Iterator(迭代器) 这个模式已经被整合入Java的Collection.在大多数场合下无需自己制造一个Iterator,只要将对象装入...
经典正则表达式 delegate vs. event 我是谁?[C#] 表达式计算引擎 正式发布表达式计算引擎WfcExp V0.9(附源码) 运算表达式类的原理及其实现 #实现的18位身份证格式验证算法 身份证15To18 的算法(C#) 一组 正则表达式...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
26.10 观察者/发布-订阅/委派事件模型(GoF) 26.11 结论 26.12 参考资料 第五部分 细化迭代3—中级主题 第27章 迭代3:中级主题 27.1 NextGen POS案例 27.2 Monopoly案例 第28章 UML活动图及其建模 28.1 ...
栈是一种线形集合,其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。 堆是栈的一个组成元素 22、forward 和redirect的区别 forward是服务器请求资源,服务器直接访问目标地址的URL,把...
ChatGPT是美国人工智能研究实验室OpenAI新推出的一种人工智能技术驱动的自然语言处理工具,使用了Transformer神经网络架构,也是GPT-3.5架构,这...此外,ChatGPT盈利模式尚处于探索阶段,后续商业化落地进展有待观察。
栈是一种线形集合,其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。 堆是栈的一个组成元素 19、forward 和redirect的区别 forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL...
template模板方法模式、memento备忘录模式、observer观察者模式、command命令模式、state状态模式、strategy策略模式、mediator调停者模式、interpreter解释器模式、visitor访问者模式、chain of responsibility责任...
原则是,每段代码,每处需要理解的地方,如果和总体架构相关,就要有说明。 软件领域需要简化。需要还原软件本来的面目。EDA有泛滥的趋势,软件的各个方面都需要简化。软件形态、需求分析、文档说明、开发工具等。 ...
在本文第一段代码中有一句话如下: cursor c_emp is select * from employee where emp_id=3; 其含义是定义一个游标c_emp,代表employee表中所有emp_id字段为3的结果集。当需要操作该结果集时,必须完成三步:打开...
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...