前兩篇我們自己首先實現了一個觀察者模式,我們再利用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並不是想象中的那么簡單。