首页 > 代码库 > 利用事件委托弥补观察者模式不足

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

前两篇我们自己首先实现了一个观察者模式,我们再利用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 args15      */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 params226      */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     @Override15     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     @Override23     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并不是想象中的那么简单。

 

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