异步非阻塞的观察者模式框架实现---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
参考资料:《设计模式之美》---王争