委托在觀察者模式中的java實(shí)現(xiàn)

最近在看《大話設(shè)計(jì)模式》這本書,書中內(nèi)容淺顯易懂,遺憾的是,書中的代碼實(shí)現(xiàn)用的是c#,雖然作為java程序員能看懂大致的意思,但是在遇到委托這樣的語言級別隔閡的時(shí)候,還是感覺很吃力。那么在c#中一行代碼就能實(shí)現(xiàn)的委托,在java中怎么實(shí)現(xiàn)呢?

本文參考觀察者設(shè)計(jì)模式 Vs 事件委托(java)

一:為什么要在觀察者模式中使用委托

觀察者模式如果不使用委托,就面臨著觀察者需要通過實(shí)現(xiàn)接口來統(tǒng)一update方法。這對于已經(jīng)封裝完成的功能來說,要求過于苛刻。那么如果在觀察者模式中使用委托,就可以擺脫掉實(shí)現(xiàn)接口統(tǒng)一方法的必要,從而完成真正意義上的類與類之間的解耦,滿足開閉原則。

二:委托在觀察者模式中的實(shí)現(xiàn)原理。

java中的委托說到底還是通過反射來實(shí)現(xiàn)。當(dāng)觀察者(observer)需要訂閱某一主題(subject)時(shí),之前是僅需要傳遞觀察者對象本身,而此時(shí)要使用委托,就額外需要傳遞觀察者接到通知后需要調(diào)用的方法名稱以及方法需要的實(shí)際參數(shù)。形如

notifier.addListener(doa, "doaUpdate", 123,"spring");

第一個(gè)參數(shù)是觀察者自己,第二個(gè)參數(shù)字符串就是需要傳遞的方法名,第三個(gè)就是需要傳遞的實(shí)際參數(shù)組成的數(shù)組。反射正是通過這樣的訂閱獲得了需要的類名,方法名和參數(shù)類型,從而實(shí)現(xiàn)了反射,實(shí)現(xiàn)了不需要接口統(tǒng)一方法名稱就可以動(dòng)態(tài)實(shí)現(xiàn)通知。

三:委托在java中的代碼實(shí)現(xiàn)。

1:Event事件類

主要是通過獲取到的類名,方法名,參數(shù)類型完成反射調(diào)用

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()調(diào)用該方法,
    //該方法通拿到每一個(gè)具體的傳入的對象和傳入的方法名,經(jīng)過反射調(diào)用對應(yīng)的方法
    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){
            // 通過反射調(diào)用對應(yīng)的方法
            e.invoke();
        }
    }
}

上面的objects.add(event)實(shí)際上就是在保存?zhèn)鬟M(jìn)來的觀察者。

3:Notifier

提供了訂閱的抽象主題,主題持有EventHandler,用來傳遞參數(shù)給Event,以及提供了何時(shí)調(diào)用通知的抽象方法。

public abstract class Notifier {

    private EventHandler eventHandler=new EventHandler();
    
    public EventHandler getEventHandler() {
        return eventHandler;
    }
  
    public  void addListener(Object object,String methodName,Object...args){
        //講傳遞進(jìn)來的參數(shù)交由EventHandler,EventHandler交由event處理。
            eventHandler.addEvent(object, methodName, args);
    }
    //具體的實(shí)現(xiàn)類規(guī)定了何時(shí)開始通知觀察者。
    public abstract void notifyx();
}
4:GoodNofitier

只關(guān)心何時(shí)需要通知觀察者就可以了。

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方法調(diào)用
//委托 觀察者
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反射時(shí)int類型參數(shù)導(dǎo)致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)

這個(gè)時(shí)候我們只需要把具體觀察者的方法中的int改為Integer類型就可以了。

public class DeleObserverA {

    public void doaUpdate(Integer name,String month){
        System.out.println("DeleObserverA 開始更新:"+name+":"+month);
    }
}

這樣就實(shí)現(xiàn)了委托在觀察者模式中的使用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1 場景問題# 1.1 訂閱報(bào)紙的過程## 來考慮實(shí)際生活中訂閱報(bào)紙的過程,這里簡單總結(jié)了一下,訂閱報(bào)紙的基本流程...
    七寸知架構(gòu)閱讀 4,688評論 5 57
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,523評論 25 708
  • 我要寫一串文字來紀(jì)念我年少的輕狂,可能是亂七八糟、毫無頭緒的,沒什么寫作技巧,我就是隨心寫,看明白就好了,...
    劉芋圓的西瓜味雪糕閱讀 530評論 0 3
  • 作為一個(gè)小散,最重要的是應(yīng)該避免散戶思維。總是寄希望于虧損的恢復(fù)盈利,卻不去觀察那些發(fā)展?jié)摿Υ蟮呐9煽偸且或T絕塵,...
    kasitravel閱讀 477評論 0 0