最近在看《大話設計模式》這本書,書中內容淺顯易懂,遺憾的是,書中的代碼實現用的是c#,雖然作為java程序員能看懂大致的意思,但是在遇到委托這樣的語言級別隔閡的時候,還是感覺很吃力。那么在c#中一行代碼就能實現的委托,在java中怎么實現呢?
一:為什么要在觀察者模式中使用委托
觀察者模式如果不使用委托,就面臨著觀察者需要通過實現接口來統一update方法。這對于已經封裝完成的功能來說,要求過于苛刻。那么如果在觀察者模式中使用委托,就可以擺脫掉實現接口統一方法的必要,從而完成真正意義上的類與類之間的解耦,滿足開閉原則。
二:委托在觀察者模式中的實現原理。
java中的委托說到底還是通過反射來實現。當觀察者(observer)需要訂閱某一主題(subject)時,之前是僅需要傳遞觀察者對象本身,而此時要使用委托,就額外需要傳遞觀察者接到通知后需要調用的方法名稱以及方法需要的實際參數。形如
notifier.addListener(doa, "doaUpdate", 123,"spring");
第一個參數是觀察者自己,第二個參數字符串就是需要傳遞的方法名,第三個就是需要傳遞的實際參數組成的數組。反射正是通過這樣的訂閱獲得了需要的類名,方法名和參數類型,從而實現了反射,實現了不需要接口統一方法名稱就可以動態實現通知。
三:委托在java中的代碼實現。
1:Event事件類
主要是通過獲取到的類名,方法名,參數類型完成反射調用
public class Event {
private Object object;
private String methodName;
private Object [] params;
private Class [] paramTypes;
public Event(Object object,String methodName,Object...objects ){
this.object=object;
this.methodName=methodName;
this.params=objects;
contractParamTypes(this.params);
}
private void contractParamTypes(Object [] params){
if(params==null){
return;
}
paramTypes=new Class[params.length];
for(int i=0;i<params.length;i++){
paramTypes[i]=params[i].getClass();
}
}
// EventHandler通過nofityx()調用該方法,
//該方法通拿到每一個具體的傳入的對象和傳入的方法名,經過反射調用對應的方法
public void invoke() throws Exception{
Method method =object.getClass().getMethod(methodName, paramTypes);
if(method==null){
}else{
method.invoke(object, params);
}
}
}
2:EventHandler
主要用來保存觀察者,以及提供通知觀察者的方法。
public class EventHandler {
private List<Event> objects;
public EventHandler(){
objects=new ArrayList<Event>();
}
public void addEvent(Object object,String methodName,Object...args){
Event event=new Event(object,methodName,args);
objects.add(event);
}
public void notifyx() throws Exception{
for(Event e:objects){
// 通過反射調用對應的方法
e.invoke();
}
}
}
上面的objects.add(event)實際上就是在保存傳進來的觀察者。
3:Notifier
提供了訂閱的抽象主題,主題持有EventHandler,用來傳遞參數給Event,以及提供了何時調用通知的抽象方法。
public abstract class Notifier {
private EventHandler eventHandler=new EventHandler();
public EventHandler getEventHandler() {
return eventHandler;
}
public void addListener(Object object,String methodName,Object...args){
//講傳遞進來的參數交由EventHandler,EventHandler交由event處理。
eventHandler.addEvent(object, methodName, args);
}
//具體的實現類規定了何時開始通知觀察者。
public abstract void notifyx();
}
4:GoodNofitier
只關心何時需要通知觀察者就可以了。
public class GoodNofitier extends Notifier{
@Override
public void notifyx(){
try {
getEventHandler().notifyx();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5:具體的觀察者
public class DeleObserverA {
public void doaUpdate(int name,String month){
System.out.println("DeleObserverA 開始更新:"+name+":"+month);
}
}
public class DeleObserverB {
public void dobUpdate(int name,String month){
System.out.println("DeleObserverB 開始更新:"+name+":"+month);
}
}
6:main方法調用
//委托 觀察者
Notifier notifier=new GoodNofitier();
DeleObserverA doa=new DeleObserverA();
DeleObserverB dob=new DeleObserverB();
Student studnet=new Student();
// notifier.addListener(doa, "doaUpdate", new Object[]{123,"spring"});
// notifier.addListener(dob, "dobUpdate", new Object[]{456,"summer"});
notifier.addListener(doa, "doaUpdate", 123,"springg");
notifier.addListener(dob, "dobUpdate", 456,"summerr");
notifier.notifyx();
7:JDK8反射時int類型參數導致NoSuchMethodException
java.lang.NoSuchMethodException: observer.delete.DeleObserverA.doaUpdate(java.lang.Integer, java.lang.String)
at java.lang.Class.getMethod(Class.java:1786)
at observer.delete.Event.invoke(Event.java:31)
at observer.delete.EventHandler.notifyx(EventHandler.java:22)
at observer.delete.GoodNofitier.notifyx(GoodNofitier.java:9)
at factorymode.MainActivity.main(MainActivity.java:139)
這個時候我們只需要把具體觀察者的方法中的int改為Integer類型就可以了。
public class DeleObserverA {
public void doaUpdate(Integer name,String month){
System.out.println("DeleObserverA 開始更新:"+name+":"+month);
}
}
這樣就實現了委托在觀察者模式中的使用。