観察者モードおよびGuava EventBus
作成は容易ではありませんが、転載は明記してください(http://shihlei.iteye.com/blog/2426888)!
概要
最近RxJavaを見るとスケルトンが使用するオブザーバーモードなのでオブザーバーモードについてまとめておきますが、ついでにGuava EventBusの実現を記録しておきます(イベントリスニングは、実はオブザーバーモードにも相当します)
二観察者モード
1)概要
オブザーバーモード:動作モードで、1対のマルチリレーションシップバインドオブジェクトの方法を提供し、1つのオブジェクトの状態が変更され、バインドオブジェクトは自分のビジネス更新を完了する通知を受け取ることができます.
主要メンバー:
被観察者(Observerable):状態が変化し、すべての観察者に通知される.
オブザーバー(observer):「オブザーバー」の状態変化通知を受信し、自分のビジネスを実行します.
シーンを使用:
「被観察者」の状態変化は、複数の「観察者」に通知する必要があるが、「被観察者」は、観察者の個数の具体的な詳細を知る必要はなく、互いに独立している.一般的に「オブザーバー」は互いに独立しており、互いに影響しません.
2)Demo
package x.rx.observer;
import java.util.LinkedList;
import java.util.List;
/**
* :
* 1) (Observerable): , ,
* 2) (Observer): " " , ,
*
* @author shilei
*/
public class ObserverPatternDemo {
public static void main(String[] args) {
Observerable observerable = new Observerable();
observerable.register(() -> {
System.out.println("observer1 handle finish!");
}).register(() -> {
System.out.println("observer2 handle finish!");
});
observerable.generateEvent();
}
/**
*
*/
interface Observer {
void doEvent();
}
/**
*
*/
static class Observerable {
private List observerList = new LinkedList<>();
/**
*
*
* @param observer
*/
Observerable register(Observer observer) {
observerList.add(observer);
return this;
}
/**
*
*/
Observerable unRegister(Observer observer) {
observerList.remove(observer);
return this;
}
/**
*
*/
void generateEvent() {
for (Observer observer : observerList) {
observer.doEvent();
}
}
}
}
三Guava EventBus
1)概要
Guava EventBusはイベントリスナーモードを実現し、主に注釈ベースのイベントバスを提供し、柔軟に使用することができる.
使用方法:
1)注目イベントを定義する
2)リスニング処理方法を定義し,@Subscribeタグでビジネスロジックを実現する
3)EventBusに登録すればよい
2)Demo
(1)依存
com.google.guava
guava
23.0
(2)コード
package x.rx.eventbus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
/**
* guava eventbus
*
* @author shilei
*/
public class EventBusDemo {
public static void main(String[] args) {
EventBus eventBus = new EventBus("demo");
//
eventBus.register(new EventListener());
//
eventBus.post(new Event());
}
/**
*
*/
static class Event {
}
/**
*
*/
static class EventListener {
@Subscribe //
public void handle1(Event event) {
System.out.println("handle1 finish! ");
}
@Subscribe
public void handle2(Event event) {
System.out.println("handle2 finish! ");
}
}
}
3)ソース分析
(1)イベント登録バス:EventBusのregister()
private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
/**
* Registers all subscriber methods on {@code object} to receive events.
*
* @param object object whose subscriber methods should be registered.
*/
public void register(Object object) {
subscribers.register(object);
}
「被観察者」のコアは、通知が必要なすべての観察者を維持するキューを提供し、Guava EventBusというワークエージェントに与えられた. SubscriberRegistry , SubscriberRegistryは、イベント処理クラスへの任意のタイプのイベントのバインド関係を提供します.
登録の詳細を確認します.
/**
* All registered subscribers, indexed by event type.
*
* The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an
* immutable snapshot of all current subscribers to an event without any locking.
*/
private final ConcurrentMap, CopyOnWriteArraySet> subscribers =
Maps.newConcurrentMap();
/**
* Registers all subscriber methods on the given listener object.
*/
void register(Object listener) {
Multimap, Subscriber> listenerMethods = findAllSubscribers(listener);
for (Map.Entry, Collection> entry : listenerMethods.asMap().entrySet()) {
Class> eventType = entry.getKey();
Collection eventMethodsInListener = entry.getValue();
CopyOnWriteArraySet eventSubscribers = subscribers.get(eventType);
if (eventSubscribers == null) {
CopyOnWriteArraySet newSet = new CopyOnWriteArraySet();
eventSubscribers =
MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
}
eventSubscribers.addAll(eventMethodsInListener);
}
}
内部はConcrurentMap、keyはイベントclass、valueは CopyOnWriteArraySet通知用、最も重要な方法 findAllSubscribers(listener) ,彼は反射して見つけます @Subscribe注記のメソッドは、バインドされたイベントに関連付けられます.取得したら追加 subscribers(タイプConcrurentMap)では、このsubscribersはすべてのイベントとイベントプロセッサのバインド関係を維持します.
findAllSubscribers(listener)を見てみましょう の詳細:
/**
* Returns all subscribers for the given listener grouped by the type of event they subscribe to.
*/
private Multimap, Subscriber> findAllSubscribers(Object listener) {
Multimap, Subscriber> methodsInListener = HashMultimap.create();
Class> clazz = listener.getClass();
for (Method method : getAnnotatedMethods(clazz)) {
Class>[] parameterTypes = method.getParameterTypes();
Class> eventType = parameterTypes[0];
methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
}
return methodsInListener;
}
通常の反射とAnnotation処理ですが、イベントは処理方法の最初のパラメータに違いありません.
これでEventBusには通知する購読者のリストがあります
(2)イベントのコミット,サブスクライバメソッドの呼び出し
ここでは簡単です.イベントのタイプによって、 subscribers(タイプConcrurentMap)は、対応するサブスクライバのセットを取得し、反射呼び出しでメソッドをok
/**
* Posts an event to all registered subscribers. This method will return successfully after the
* event has been posted to all subscribers, and regardless of any exceptions thrown by
* subscribers.
*
* If no subscribers have been subscribed for {@code event}'s class, and {@code event} is not
* already a {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted.
*
* @param event event to post.
*/
public void post(Object event) {
Iterator eventSubscribers = subscribers.getSubscribers(event);
if (eventSubscribers.hasNext()) {
dispatcher.dispatch(event, eventSubscribers);
} else if (!(event instanceof DeadEvent)) {
// the event had no subscribers and was not itself a DeadEvent
post(new DeadEvent(this, event));
}
}
(3)まとめ:
EventBusはオブザーバーモードで汎用的な抽象化を行い,任意のイベントを定義し,注釈ベースのイベントプロセッサとして非常に有用である.