观察者模式中的角色可以分为Subject和Observer,它们分别为被观察者和观察者,观察者观察被观察者的状态变更。google的guava中提供了EventBus功能,EventBus可以实现观察者模式,下面是一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

public class TestEventBus {

public static void main(String[] args) {
EventBus eventBus = new EventBus("test");
// 注册一个listener
eventBus.register(new Listener());
// 遍历被注册的对象以及其方法,调用符合要求的方法
eventBus.post(new Event("hello, world"));
}

}

class Event {
public String message;

Event(String message) {
this.message = message;
}
}

class Listener {
@Subscribe
public void listen(Event event) {
System.out.println(event.message);
}
}

我们也可以自己实现一个EventBus,虽然在效率上没办法和google的实现相比,但是基本原理都是一样的。
首先我们创建一个用于标记方法是否需要实现订阅的注解

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
}

随后创建EventBus的核心类,其主要功能是添加或移除listener,以及根据传入的参数对符合要求的方法进行调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class EventBus {

private Set<Object> listeners = new HashSet<>();

public void register(Object listener) {
listeners.add(listener);
}

public void unregister(Object listener) {
listeners.remove(listener);
}

// 1. 遍历所有的listener对象;
// 2. 遍历所有的listener对象的方法;
// 3. 判断该方法是否添加了订阅注解,方法的参数个数和参数类型是否符合调用方的参数要求;
// 4. 如果一切条件都符合,执行该方法;
public void post(Object event) {
for (Object listener : listeners) {
Method[] methods = listener.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Subscribe.class)) {
Class[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0] == event.getClass()) {
try {
// 调用方法
method.invoke(listener, event);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}

}

最后我们对上面的实现进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {

public static void main(String[] args) {
EventBus eventBus = new EventBus();
eventBus.register(new Listener());
eventBus.post("Mike");
}

}

class Listener {
@Subscribe
public void say(String name) {
System.out.println("hello, " + name);
}
}

测试代码打印出了如下结果

hello, Mike

参考:
https://juejin.im/post/5b61c852e51d451956055476
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/25