利用事件委托弥补观察者模式不足
前两篇我们自己首先实现了一个观察者模式,我们再利用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并不是想象中的那么简单。
不积跬步,无以至千里;不积小流,无以成江海。