利用事件委托弥补观察者模式不足

Posted CoderBuff

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用事件委托弥补观察者模式不足相关的知识,希望对你有一定的参考价值。

前两篇我们自己首先实现了一个观察者模式,我们再利用Java自带的接口和类实现了观察者模式,但其实两种观察者模式存在不足的地方。之前两种观察者模式的观察者(订阅者)都是实现了一个同一个接口,实现了接口中的update方法,但是如果两个观察者风马牛不相及,完全无关呢?或者他们的方法名不一样这个时候该怎么办呢?《大话设计模式》中C#提供了事件委托,但在Java中比没有提供。此时,我们可以利用Java的反射机制来实现事件委托从而来弥补观察者模式的不足。

我们先来看看客户端的测试代码,直观的感受一下和之前的观察者模式有什么不同。

 1 package day_11_event;
 2 
 3 import java.util.Date;
 4 
 5 /**
 6  * 利用事件委托
 7  * @author turbo
 8  *
 9  * 2016年9月16日
10  */
11 public class Main {
12 
13     /**
14      * @param args
15      */
16     public static void main(String[] args) {
17         Notifier notifier = new ConcreteNotifier();
18         Observer1 observer1 = new Observer1();
19         Observer2 observer2 = new Observer2();
20         
21         notifier.attach(observer1, "changeState1", new Date());
22         notifier.attach(observer2, "changeState2", new Date());
23         
24         notifier.notifyObj();
25     }
26 
27 }

我们的观察者observer1、observer2是两个不相关的观察者,可以看到两者需要改变状态的方法分别是changeState1、changeState2,如果使用之前的观察者模式则是实现同一个接口实现同一个方法。如果遇到现在这种情况,则没办法了。

由于Java并没有为我们提供事件委托,我们首先需要自己实现一个事件类。

 1 package day_11_event;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 /**
 6  * 事件类
 7  * @author turbo
 8  *
 9  * 2016年9月16日
10  */
11 public class Event {
12     private Object object;
13     private String methodName;
14     private Object[] params;
15     private Class[]    paramsType;
16     
17     public Event(Object object, String methodName, Object...args){
18         this.object = object;
19         this.methodName = methodName;
20         this.params = args;
21         contractParamTypes(this.params);
22     }
23 
24     /**
25      * @param params2
26      */
27     private void contractParamTypes(Object[] params2) {
28         this.paramsType = new Class[params2.length];
29         for (int i = 0; i < params.length; i++){
30             this.paramsType[i] = params[i].getClass();
31         }
32     }
33     
34     public void invoke() throws Exception{
35         Method method = object.getClass().getMethod(this.methodName, this.paramsType);
36         if (null == method){
37             return ;
38         }
39         method.invoke(this.object, this.params);
40     }
41 }

利用反射实现一个事件类后,我们还需要实现一个管理事件的类。

 1 package day_11_event;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * 管理事件类
 8  * @author turbo
 9  *
10  * 2016年9月16日
11  */
12 public class EventHandler {
13     private List<Event> objects;
14     
15     public EventHandler(){
16         objects = new ArrayList<Event>();
17     }
18     
19     public void addEvent(Object object, String methodName, Object...args){
20         objects.add(new Event(object, methodName, args));
21     }
22     
23     public void notifyObj() throws Exception{
24         for (Event event : objects){
25             event.invoke();
26         }
27     }
28 }

这样我们就利用Java完成了我们事件委托的基本模型。

基本工作做好后,我们来实现观察者模式。

 1 package day_11_event;
 2 
 3 /**
 4  * 抽象通知者
 5  * @author turbo
 6  *
 7  * 2016年9月16日
 8  */
 9 public abstract class Notifier {
10     private EventHandler eventHandler = new EventHandler();
11 
12     public EventHandler getEventHandler() {
13         return eventHandler;
14     }
15 
16     public void setEventHandler(EventHandler eventHandler) {
17         this.eventHandler = eventHandler;
18     }
19 
20     public abstract void attach(Object object, String methodName, Object...args);
21     
22     public abstract void notifyObj();
23 }
 1 package day_11_event;
 2 
 3 /**
 4  * 具体通知者
 5  * @author turbo
 6  *
 7  * 2016年9月16日
 8  */
 9 public class ConcreteNotifier extends Notifier {
10 
11     /* (non-Javadoc)
12      * @see day_11_event.Notifier#attach(java.lang.Object, java.lang.String, java.lang.Object[])
13      */
14     @Override
15     public void attach(Object object, String methodName, Object... args) {
16         this.getEventHandler().addEvent(object, methodName, args);
17     }
18 
19     /* (non-Javadoc)
20      * @see day_11_event.Notifier#notifyObj()
21      */
22     @Override
23     public void notifyObj() {
24         try {
25             this.getEventHandler().notifyObj();
26         } catch (Exception e) {
27             e.printStackTrace();
28         }
29     }
30 
31 }

通知者我们同样还是定义了一个抽象类,并实现了一个具体的通知类。

下面是两个观察者,两个观察者没有任何关系,不必实现同一个接口。

 1 package day_11_event;
 2 
 3 import java.util.Date;
 4 
 5 /**
 6  * 观察者1
 7  * @author turbo
 8  *
 9  * 2016年9月16日
10  */
11 public class Observer1 {
12     public Observer1(){
13         System.out.println("Observer1状态1");
14     }
15     
16     public void changeState1(Date date){
17         System.out.println("Observer1改变状态" + date);
18     }
19 }
 1 package day_11_event;
 2 
 3 import java.util.Date;
 4 
 5 /**
 6  * 观察者2
 7  * @author turbo
 8  *
 9  * 2016年9月16日
10  */
11 public class Observer2 {
12     public Observer2(){
13         System.out.println("Obsever2状态1");
14     }
15     
16     public void changeState2(Date date){
17         System.out.println("Observer2改变状态" + date);
18     }
19 }

客户端在开头给出,这里我们不再给出。利用事件委托确实为我们解决了观察者完全不相关,但是又想他们俩都收到通知的难题。这得归功于Java的反射机制,在之前的抽象工厂模式中我们也利用了Java的反射机制。看似平时用得不多的反射,但是却能为我们做很多事情,Java并不是想象中的那么简单。

 

以上是关于利用事件委托弥补观察者模式不足的主要内容,如果未能解决你的问题,请参考以下文章

观察者模式与事件委托

C# 委托和事件 与 观察者模式(发布-订阅模式)讲解 by天命

《C#零基础入门之百识百例》(七十八)委托事件实例练习3 -- 观察者模式

《大话设计模式》——读后感 老板回来了,我不知道?——观察者模式之事件委托

观察者设计模式 Vs 事件委托(java)

事件委托实现观察者模式