java里的回調(diào)與事件委托

java里的回調(diào)與事件委托

1、java里的回調(diào)是什么?

這里講解的很詳細(xì),http://blog.csdn.net/veryitman/article/details/6937468

就是A類中調(diào)用B類中的某個(gè)方法c,然后B類中c反過來調(diào)用A類中的方法d,d這個(gè)方法就叫回調(diào)方法。

其實(shí)strategy**、template**、observer**visitor**模式全部都是回調(diào)的不同應(yīng)用。簡(jiǎn)單來說就是本來可以寫死在一起的代碼給拆開來,讓其中一塊保持原有的流程,并在流程中挖出一些空,讓另一塊代碼作為參數(shù)傳進(jìn)來在流程中合適的地方被調(diào)用,也就是UML里所說的聚合。

2、委托到底是什么?

java中沒有委托,但是java中有個(gè)委托事件的模型。

參考 http://blog.csdn.net/yanshujun/article/details/6494447

1.1、觀察者模式與事件委托對(duì)比

可以參考博文:http://blog.csdn.net/gdutxiaoxu/article/details/51824769

1.2、事件委托

看個(gè)例子,我們先來看一下 我們的通知者GoodNotifier是怎樣實(shí)現(xiàn)的?

public class GoodNotifier extends Notifier {   

    @Override  
    public void addListener(Object object, String methodName, Object... args) {   
        System.out.println("有新的同學(xué)委托盡職盡責(zé)的放哨人!");   
        EventHandler handler = this.getEventHandler();
        handler.addEvent(object, methodName, args);   
    }   

    @Override  
    public void notifyX() {   
        System.out.println("盡職盡責(zé)的放哨人告訴所有需要幫忙的同學(xué):老師來了");   
        try{   
            this.getEventHandler().notifyX();   
        }catch(Exception e){   
            e.printStackTrace();   
        }   
    }   

}  

接著我們?cè)趤?看它的父類Notifier是怎樣實(shí)現(xiàn)的

  /**
   * 通知者的 抽象類
   */
  public abstract class Notifier {
      private EventHandler eventHandler = new EventHandler();
      public EventHandler getEventHandler() {
          return eventHandler;
      }
      public void setEventHandler(EventHandler eventHandler) {
          this.eventHandler = eventHandler;
      }
      /**
       * 增加需要幫忙 放哨 的 學(xué)生
       * 
       * @param object 要執(zhí)行方法的對(duì)象
       * @param methodName 執(zhí)行方法 的方法名
       * @param args   執(zhí)行方法的參數(shù)
       */
      public abstract void addListener(Object object, String methodName, Object... args);
      /**
       * 告訴所有要幫忙放哨的學(xué)生:老師來了
       */
      public abstract void notifyX();
  }

看了上面notifier的代碼以后,你是不是有一種似曾相識(shí)的感覺,是不是感覺跟我們的觀察者模式中的通知者很相似

  • 觀察者模式我們的通知者是這樣實(shí)現(xiàn)的

    @Override
        public void notifyAllObserver(Object data) {
            for (Observer observer : mList) {
                observer.update(this,data);
            }
    
        }
    
  • 在事件委托中我們是這樣實(shí)現(xiàn)的

   @Override  
      public void notifyX() {   
          System.out.println("盡職盡責(zé)的放哨人告訴所有需要幫忙的同學(xué):老師來了");   
          try{   
              EventHandler handler = this.getEventHandler();
              handler.notifyX();   
          }catch(Exception e){   
              e.printStackTrace();   
          }   
      }  
  • 兩者的區(qū)別其實(shí)即使我們將其交給EventHandler去處理

    下面我們來看一下EventHandler我們是怎樣實(shí)現(xiàn)的

  /**
   * 事件的 處理者
   */
  public class EventHandler {   
      //是用一個(gè)List   
      private List<Event> objects;   
      public EventHandler(){   
          objects=new ArrayList<Event>();   
      }   
      //添加某個(gè)對(duì)象要執(zhí)行的事件,及需要的參數(shù)   
      public void addEvent(Object object,String methodName,Object...args){   
          objects.add(new Event(object,methodName,args));   
      }   
      //通知所有的對(duì)象執(zhí)行指定的事件   
      public void notifyX() throws Exception{   
          for(Event e : objects){   
              e.invoke();   
          }   
      }   
  } 

其實(shí)跟觀察者 模式一樣,我們把我們的事件對(duì)象存儲(chǔ)在

  objects=new ArrayList<Event>();  

當(dāng)我們添加 事件的時(shí)候,把事件封裝為Event對(duì)象,再添加到objects中

  objects.add(new Event(object,methodName,args));   

當(dāng)我們要通知事件的時(shí)候,再遍歷List,通知每一個(gè)事件對(duì)象

  for(Event e : objects){   
       e.invoke();   
  } 

看到invoke()這個(gè)方法,你是不是想到了什么,沒錯(cuò),就是java的反射機(jī)制,我們封裝在Event類 中,下面我們來看一下Event類是怎樣實(shí)現(xiàn)的

  /**
   * 事件對(duì)象的封裝類
   */
  public class Event {   
      //要執(zhí)行方法的對(duì)象   
      private Object object;   
      //要執(zhí)行的方法名稱   
      private String methodName;   
      //要執(zhí)行方法的參數(shù)   
      private Object[] params;   
      //要執(zhí)行方法的參數(shù)類型   
      private Class[] paramTypes;   
      public Event(){}   
      public Event(Object object,String methodName,Object...args){   
          this.object=object;   
          this.methodName=methodName;   
          this.params=args;   
          contractParamTypes(this.params);   
      }   
      //根據(jù)參數(shù)數(shù)組生成參數(shù)類型數(shù)組   
      private void contractParamTypes(Object[] params){   
          this.paramTypes=new Class[params.length];   
          for(int i=0;i<params.length;i++){   
              this.paramTypes[i]=params[i].getClass();   
          }   
      }   


      public Object getObject() {
          return object;
      }

    //這里省略了若干get和set方法

      /**
       * 根據(jù)該對(duì)象的方法名,方法參數(shù),利用反射機(jī)制,執(zhí)行該方法  
       * @throws Exception
       */
      public void invoke() throws Exception{   
          Method method=object.getClass().getMethod(this.getMethodName(),
                           this.getParamTypes());   
          if(null==method){   
              return;   
          }   
          method.invoke(this.getObject(), this.getParams());   
      }   
  }

在Event類我們invoke()方法所做的工作就是根據(jù)我們的對(duì)象的方法名,方法參數(shù),利用發(fā)射執(zhí)行方法

運(yùn)行一下 測(cè)試代碼

  //創(chuàng)建一個(gè)盡職盡責(zé)的放哨者   
  Notifier goodNotifier=new GoodNotifier();   
  //創(chuàng)建一個(gè)玩游戲的同學(xué),開始玩游戲   
  WatchCartoonListener playingGameListener=new WatchCartoonListener();  
  //創(chuàng)建一個(gè)看電視的同學(xué),開始看電視   
  WatchingNBAListener watchingTVListener=new WatchingNBAListener();   
  //玩游戲的同學(xué)告訴放哨的同學(xué),老師來了告訴一下   
  goodNotifier.addListener(playingGameListener, "stopPlayingGame",new Date());   
  //看電視的同學(xué)告訴放哨的同學(xué),老師來了告訴一下   
  goodNotifier.addListener(watchingTVListener, "stopWatchingTV",new Date());   
  try{   
       //一點(diǎn)時(shí)間后   
       Thread.sleep(1000);   
      }catch(Exception e){   
        e.printStackTrace();   
      }   
  //老師出現(xiàn),放哨的人通知所有要幫忙的同學(xué):老師來了   
  goodNotifier.notifyX();

結(jié)果 如下

WatchCartoonListener 我正在看漫畫,開始時(shí)間:Mon Jul 04 13:25:34 CST 2016

WatchingNBAListener我正在看NBA,開始時(shí)間是: Mon Jul 04 13:25:34 CST 2016

有新的同學(xué)委托盡職盡責(zé)的放哨人!

有新的同學(xué)委托盡職盡責(zé)的放哨人!

盡職盡責(zé)的放哨人告訴所有需要幫忙的同學(xué):老師來了

WatchCartoonListener 老師來了,不要看漫畫了,結(jié)束時(shí)間:Mon Jul 04 13:25:34 CST 2016

WatchingNBAListener老師來了,快關(guān)閉NBA直播 , 結(jié)束時(shí)間是:Mon Jul 04 13:25:34 CST 2016

1.3、委托能解決什么問題?(好處)

這里,通知者類完全不知道自己需要通知的是誰,做到了完全解耦(1),同時(shí)也去掉了抽象的觀察者類(2)。觀察者模式的缺點(diǎn)是抽象通知者依賴抽象觀察者,.NET中用委托技術(shù)處理這個(gè)問題,事件委托擴(kuò)展性好(3)

即:

1.  放哨者完全不知道做游戲者的存在,完全解耦。(當(dāng)然,功勞歸功于Event和EventHandler,且這兩個(gè)類具有通用性)
2.  老師來了后游戲者停止游戲回到座位,看NBA者停止看NBA,看漫畫這停止看漫畫,玩游戲這停止玩游戲。(一次通知,執(zhí)行了不同類的不同方法)
3.  擴(kuò)展性很高,再來一個(gè)打籃球的學(xué)生就先寫個(gè)打籃球?qū)W生類,并在測(cè)試代碼中告訴放哨者一下就好,放哨者完全沒有變。重用性好

3、代理模式(Proxy Pattern)

可以翻譯成代理模式,也可以翻譯成委托模式。

java中實(shí)現(xiàn)代理:

// 抽象角色:
abstract class Subject {
    abstract public void request();
}
// 真實(shí)角色:實(shí)現(xiàn)了Subject的request()方法
class RealSubject extends Subject {
    public RealSubject() {}
    public void request() {
        System.out.println(" From real subject. ");
    }
}
// 代理角色:
class ProxySubject extends Subject {
    // 以真實(shí)角色作為代理角色的屬性
    private Subject realSubject;
    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }
    // 該方法封裝了真實(shí)對(duì)象的request方法
    public void request() {
        //preRequest();
        System.out.println("代理準(zhǔn)備...");
        // 此處執(zhí)行真實(shí)對(duì)象的request方法
        realSubject.request();
        //postRequest();
        System.out.println("代理結(jié)束...");
    }
}
public class Client {
    public static void main(String[] args) {
        RealSubject real = new RealSubject();
        Subject sub = new  ProxySubject(real);
        sub.request();
    }
}

以下是java中實(shí)現(xiàn)動(dòng)態(tài)代理的方式

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 抽象角色
interface Subject {
    void request();
}
// 具體角色RealSubject:
class RealSubject implements Subject {
    public RealSubject() {}
    public void request() {
        System.out.println(" From real subject. ");
    }
}
//動(dòng)態(tài)代理對(duì)象
class DynamicSubject implements InvocationHandler {
    private Object sub;
    public DynamicSubject() {}
    public DynamicSubject(Object obj) { sub = obj; }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(" before calling " + method);
        Object invoke = method.invoke(sub, args);
        System.out.println(" after calling " + method);
        return invoke;
    }
}
public class Client {
    static public void main(String[] args) throws Throwable {
        RealSubject rs = new RealSubject(); // 在這里指定被代理類
        InvocationHandler ds = new DynamicSubject(rs);
        Class cls = rs.getClass();
       Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ds);

        subject.request();
    }
}

java中委托事件的模型(Delegated Event Model)

六類uml關(guān)系

http://www.open-open.com/lib/view/open1328059700311.html

在idea自帶的diagram中組合和聚合都是一樣的實(shí)心箭頭,但是多了個(gè)create的虛線,

其實(shí)標(biāo)準(zhǔn)的來講組合應(yīng)該是實(shí)心箭頭,聚合應(yīng)該是虛心箭頭。

參考

[1]:http://hbohuan.blog.163.com/blog/static/208489820077132225530/

[2]:java語言實(shí)現(xiàn)事件委托模式 http://blog.csdn.net/yanshujun/article/details/6494447

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 注意:文中代碼在VS2005下通過,由于VS2003(.Net Framework 1.1)不支持隱式的委托變量,...
    胡諾閱讀 515評(píng)論 0 4
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,017評(píng)論 0 11
  • 【學(xué)習(xí)難度:★★★☆☆,使用頻率:★★★★★】直接出處:觀察者模式梳理和學(xué)習(xí):https://github.com...
    BruceOuyang閱讀 1,672評(píng)論 1 5
  • 在正文開始之前的最后,放上 GitHub 鏈接和引入依賴的 gradle 代碼: Github: https://...
    松江野人閱讀 6,163評(píng)論 0 1
  • 記一個(gè)喪病的腦洞。 區(qū)域里最后一個(gè)正常本丸和他被時(shí)之政府派來作妖的審神者。
    水草肅Gloria閱讀 197評(píng)論 0 0

友情鏈接更多精彩內(nèi)容