异步非阻塞的观察者模式框架实现---EventBus

一.普通版本的观察者模式

以下是观察者模式的模板,这里就不展开解释了

 1 public interface Observer {
 2     void print(String msg);
 3 }
 4 //被观察者1
 5 public class ConcreteObserverOne implements Observer {
 6     public void print(String msg) {
 7         System.out.println(this.getClass().getName() + " say: " + msg);
 8     }
 9 }
10 //被观察者2
11 public class ConcreteObserverTwo implements Observer {
12     public void print(String msg) {
13         System.out.println(this.getClass().getName() + " say: " + msg);
14     }
15 }
16 //被观察者3
17 public class ConcreteObserverThree implements Observer {
18     @Override
19     public void print(String msg) {
20         System.out.println(this.getClass().getName() + " say: " + msg);
21     }
22 }
23 
24 public interface Subject {
25     void attach(Observer observer);
26     void detach(Observer observer);
27     void notify(String msg);
28 }
29 //观察者
30 public class ConcreteSubject implements Subject{
31     HashSet<Observer> set = new HashSet<>();
32     @Override
33     public void attach(Observer observer) {
34         set.add(observer);
35     }
36     @Override
37     public void detach(Observer observer) {
38         set.remove(observer);
39     }
40     @Override
41     public void notify(String msg) {
42         for (Observer observer : set) {
43             observer.print(msg);
44         }
45     }
46 }
47 //测试
48 public class Demo {
49     public static void main(String[] args) {
50         Subject concreteSubject = new ConcreteSubject();
51         concreteSubject.attach(new ConcreteObserverOne());
52         concreteSubject.attach(new ConcreteObserverTwo());
53         concreteSubject.attach(new ConcreteObserverThree());
54         concreteSubject.notify("hello!!!");
55     }
56 }
57 //输出
58 ConcreteObserverTwo say: hello!!!
59 ConcreteObserverOne say: hello!!!
60 ConcreteObserverThree say: hello!!!

二.需求分析

1.基本需求

根据上述的基本模板,可以归纳出观察者模式的几个基本需求:

(1)attach()或者是register(),注册被观察者的动作

(2)notify()或者是post(),通知被观察者的动作

(3)注册表,存储所有被观察者的数据结构

(4)观察者类,包含1、2两个动作和3的数据结构的类

基于上述的需求,其实就是ConcreteSubject的实现

 

2.进阶需求

现在在上述基本需求的基础上提出进阶的需求:

(1)register()的粒度细化到方法,而不是对象

(2)异步非阻塞的通知

 

3.需求分析

别看只有一个进阶需求,但是要改的地方真的不少:

(1)注册的时候,怎么标识哪些方法需要注册?

答:可以使用自定义注解标注

 

(2)post()的实现中可以看到是对统一接口的调用(因为所有子类都继承了父类的print方法),但是当注册的粒度细化到方法,如何统一调用这些方法?

答:根据入参类型判断,post的入参传递给所有方法入参是其类或者父类的方法,这么说有些拗口,举个例子

register()注册了两个方法,method1(Bclass b)和method2(Cclass c),method1和method2的入参分别是Bclass和Cclass。

post(Aclass a)方法的入参类型是Aclass,且Aclass是Bclass的子类。

那么入参a最终会调用method1而不会调用method2。

 

(3)注册表不仅仅存储对象

答:它的数据结构应该是这样的Map<ParamType, <Object, Method>>

其中ParamType就是入参类型,Object是注册对象,Method是注册方法

 

(4)关于异步非阻塞的问题其实很好解决,只需要开辟一个线程池异步进行方法调用即可

 

三.进阶版本的观察者模式

(1)首先是自定义注解,其实这只是一个标识,没有什么别的作用

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

(2)注册表的设计

 1 private ConcurrentMap<Class<?>, CopyOnWriteArraySet<ObserverAction>> registry = new ConcurrentHashMap<>();
 2 public class ObserverAction {
 3     private Object target;
 4     private Method method;
 5 
 6     public ObserverAction(Object target, Method method) {
 7         this.target = Preconditions.checkNotNull(target);
 8         this.method = method;
 9         this.method.setAccessible(true);
10     }
11 
12     public void execute(Object event) { // event是method方法的参数
13         try {
14             method.invoke(target, event);
15         } catch (InvocationTargetException | IllegalAccessException e) {
16             e.printStackTrace();
17         }
18     }
19 }

(3)register的设计

 1 public class ObserverRegistry {
 2     private ConcurrentMap<Class<?>, CopyOnWriteArraySet<ObserverAction>> registry = new ConcurrentHashMap<>();
 3 
 4     public void register(Object observer) {
 5         Map<Class<?>, Collection<ObserverAction>> observerActions = findAllObserverActions(observer);
 6         for (Map.Entry<Class<?>, Collection<ObserverAction>> entry : observerActions.entrySet()) {
 7             Class<?> eventType = entry.getKey();
 8             Collection<ObserverAction> eventActions = entry.getValue();
 9             CopyOnWriteArraySet<ObserverAction> registeredEventActions = registry.get(eventType);
10             if (registeredEventActions == null) {
11                 registry.putIfAbsent(eventType, new CopyOnWriteArraySet<>());
12                 registeredEventActions = registry.get(eventType);
13             }
14             registeredEventActions.addAll(eventActions);
15         }
16     }
17     
18     //构建类的注册表
19     private Map<Class<?>, Collection<ObserverAction>> findAllObserverActions(Object observer) {
20         Map<Class<?>, Collection<ObserverAction>> observerActions = new HashMap<>();
21         Class<?> clazz = observer.getClass();
22         for (Method method : getAnnotatedMethods(clazz)) {
23             Class<?>[] parameterTypes = method.getParameterTypes();
24             Class<?> eventType = parameterTypes[0];
25             if (!observerActions.containsKey(eventType)) {
26                 observerActions.put(eventType, new ArrayList<>());
27             }
28             observerActions.get(eventType).add(new ObserverAction(observer, method));
29         }
30         return observerActions;
31     }
32     
33     //获取类的注解方法
34     private List<Method> getAnnotatedMethods(Class<?> clazz) {
35         List<Method> annotatedMethods = new ArrayList<>();
36         for (Method method : clazz.getDeclaredMethods()) {
37             if (method.isAnnotationPresent(Subscribe.class)) {
38                 Class<?>[] parameterTypes = method.getParameterTypes();
39                 Preconditions.checkArgument(parameterTypes.length == 1,
40                         "Method %s has @Subscribe annotation but has %s parameters."
41                                 + "Subscriber methods must have exactly 1 parameter.",
42                         method, parameterTypes.length);
43                 annotatedMethods.add(method);
44             }
45         }
46         return annotatedMethods;
47     }
48 }

(3)post的设计

 1 public class EventBus {
 2     private Executor executor;
 3     private ObserverRegistry registry = new ObserverRegistry();
 4 
 5     public EventBus() {
 6         this(MoreExecutors.directExecutor());
 7     }
 8 
 9     protected EventBus(Executor executor) {
10         this.executor = executor;
11     }
12 
13     public void register(Object object) {
14         registry.register(object);
15     }
16 
17     public void post(Object event) {
18         List<ObserverAction> observerActions = registry.getMatchedObserverActions(event);
19         for (ObserverAction observerAction : observerActions) {
20             executor.execute(new Runnable() {
21                 @Override
22                 public void run() {
23                     observerAction.execute(event);
24                 }
25             });
26         }
27     }
28 }

(4)测试

 1 public class Fish {
 2     @Subscribe
 3     public void bubble(Integer a){
 4         System.out.println("fish:" + a);
 5     }
 6 }
 7 public class Dog {
 8     @Subscribe
 9     public void wang(String a){
10         System.out.println("dog:" + a);
11     }
12 }
13 public class Cat {
14     @Subscribe
15     public void miao(String a){
16         System.out.println("cat:" + a);
17     }
18 }
19 public class Demo {
20     public static void main(String[] args) {
21         EventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(10));
22         eventBus.register(new Dog());
23         eventBus.register(new Cat());
24         eventBus.register(new Fish());
25 
26         eventBus.post("test");
27     }
28 }
29 //结果(因为post的入参是String,而只有Cat和Dog的方法入参是String)
30 dog:test
31 cat:test

 

参考资料:《设计模式之美》---王争

 

 

posted @ 2020-11-23 22:57  kozz  阅读(392)  评论(1编辑  收藏  举报