Java 回調函數的使用

回調函數

回調函數是什麼鬼, 回調函數幹嗎用,回調函數能夠怎麼用java

若是有過android開發經驗,常常能夠看到一些相似下面的代碼android

Button Btn1 = (Button)findViewById(R.id.button1);//獲取按鈕資源    
Btn1.setOnClickListener(new Button.OnClickListener(){//建立監聽    
    public void onClick(View v) {    
        String strTmp = "點擊Button01";    
        Ev1.setText(strTmp);    
    }    

});

上面註冊的監聽器其實就包含了這個回調的意味了。git

軟件模塊之間老是存在着必定的接口,從調用方式上,能夠把他們分爲三類:同步調用、回調和異步調用。
同步調用:一種阻塞式調用,調用方要等待對方執行完畢才返回,它是一種單向調用; 回調:一種雙向調用模式,也就是說,被調用方在接口被調用時也會調用對方的接口; 異步調用:一種相似消息或事件的機制,不過它的調用方向恰好相反,接口的服務在收到某種訊息或發生某種事件時,會主動通知客戶方(即調用客戶方的接口)。github

回調和異步調用的關係很是緊密:使用回調來實現異步消息的註冊,經過異步調用來實現消息的通知web

所謂回調,就是客戶程序CLIENT調用服務程序SERVER中的某個函數SA(),而後SERVER又在某個時候反過來調用CLIENT中的某個函數mycallback(),對於CLIENT來講,這個mycallback便叫作回調函數。例如Win32下的窗口過程函數就是一個典型的回調函數。apache

簡單來講,就是在調用一個組建的方法時,按照他的定義,註冊一個咱們本身的方法,期待這個組建在某一個特意場景下調用咱們註冊的方法,實現對應的功能api

設計回調函數的思路

上面簡單的說明了什麼是回調函數,那麼怎麼去設計一個回調函數呢?緩存

首先能夠明確的是調用方要實現一個註冊方法,被調用方提供一個功能,在某些特定的狀況下,會調用註冊方法java-web

如一個應用場景是:異步

如查詢一條微博的點贊數,在db中保存了用戶+微博的點贊映射關係,一般須要獲取點贊數的時候,須要在db中count一下,計算點贊數,當點贊數不少的時候,改怎麼辦?每次都count一把? 性能開銷難以接受

一個簡單的方法是使用緩存,將點贊數保存在緩存中,每次獲取點贊數都從緩存取,緩存沒有命中的時候,才從db中count一把,並回寫到緩存中

上面這個應用場景該如何設計成回調函數的形式呢?

  1. 一個緩存客戶端,負責從緩存中獲取數據 CacheClient
  2. 業務方,對外提供查詢點贊數的功能 (一個對外的getCount()接口,一個內部的查詢db計算總數的接口)

通常爲了通用性而言,CacheClient內部若是將緩存未命中查db的功能代碼封裝起來,會有什麼問題?耦合過高,無法複用

so 形式話的結構以下:

CacheClient:

  1. 回調接口 CallableInterface
  2. 緩存操做類
  3. 註冊回調函數類

使用方 CountService:

  1. db中查詢評價總數的方法(否則緩存中的初始數據從哪裏來?)
  2. 具體的業務使用邏輯代碼

實例

看上面的描述能看懂麼?臥槽,本身寫的東西本身都看不大懂啊,果真仍是代碼是王道,先看看代碼,看一下是怎麼玩的,而後在回過頭去看一下上面的,效果會好不少

註冊器相關類:

回調接口 CacheCallBackInterface

package com.mushroom.hui.common.register.callback;

/**
 * 緩存未命中的回調函數
 * Created by yihui on 16/4/5.
 */
public interface CacheCallBackInterface {

    String getKey(int id);

    int getExpire();

    Object getObject(String key);
}

註冊接口 BaseRegister.java --> 爲何要設計成接口?看CacheCient的時候會理解一點的

package com.mushroom.hui.common.register;


import com.mushroom.hui.common.register.callback.CacheCallBackInterface;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by yihui on 16/4/5.
 */
public interface BaseRegister {
    Map<String, CacheCallBackInterface> map = new HashMap<>();

    void register(String name, CacheCallBackInterface callback) throws Exception;

    Object exec(String name, int id) throws Exception;

    <T> T exec(String name, int id, Class<T> clz) throws Exception;
}

CacheClient.java 對外提供的緩存客戶端, 這個裏面就實現了傳說中的回調函數的使用

package com.mushroom.hui.common.cache;

import com.mushroom.hui.common.cache.api.CacheInterface;
import com.mushroom.hui.common.register.BaseRegister;
import com.mushroom.hui.common.register.callback.CacheCallBackInterface;
import com.mushroom.hui.common.register.exception.RegisterException;
import org.apache.commons.lang.StringUtils;

import javax.annotation.Resource;

/**
 * Created by yihui on 16/4/5.
 */
public class CacheClient implements BaseRegister {

    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CacheClient.class);

    @Resource(name = "cacheService")
    private CacheInterface methodCache;


    @Override
    public void register(String name, CacheCallBackInterface callback) throws Exception {
        if(StringUtils.isBlank(name)) {
            throw new IllegalArgumentException("key is empty!");
        }

        if (map.containsKey(name)) {
            throw new RegisterException("this callback interface has already registered! name : " + name);
        }

        map.put(name, callback);
    }


    @Override
    public Object exec(String name, int id) throws Exception {
        return exec(name, id, Object.class);
    }


    @Override
    public <T> T exec(String name, int id, Class<T> clz) throws Exception {
        CacheCallBackInterface callBackInterface = map.get(name);
        if (callBackInterface == null) {
            throw new RegisterException("this callback interface has already registered! name : " + name);
        }

        String key = callBackInterface.getKey(id);
        Object obj = methodCache.getObject(key, clz);
        if (obj == null) {
            obj = callBackInterface.getObject(key);
            methodCache.setObject(key, obj, callBackInterface.getExpire());
        }

        return (T) obj;
    }


    /**
     * 獲取緩存的對象
     * @param name  註冊的回調函數name
     * @param id    id
     * @param clz   返回的對象類型
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> T getObject(String name, int id, Class<T> clz) throws Exception {
        return exec(name, id, clz);
    }

}

測試類中,對上面的功能進行測試,代碼以下:

@Test
public void testCacheClient() throws Exception {
    int id = 1002;
    String name = "cache_test";

    // 註冊回調函數
    cacheClient.register(name, new CacheCallBackInterface(){

        @Override
        public String getKey(int id) {
            return "test_" + id;
        }

        @Override
        public int getExpire() {
            return 30;
        }

        @Override
        public Object getObject(String key) {
            return key.length();
        }
    });

    Integer count = cacheClient.getObject(name, id ,Integer.class);
    logger.info("The count is : {}", count);


    cacheService.setObject("test_" + id, 1231234, 30);


    count = cacheClient.getObject(name, id, Integer.class);
    logger.info("The count is : {}", count);


    Thread.sleep(1000);
    count = cacheClient.getObject(name, id, Integer.class);
    logger.info("The count is : {}", count);
}

題外話

寫代碼的時候仍是很清晰,知道應該怎麼玩,而實際動手寫這個流程的時候,仍是有不少問題啊,有一些地方竟然不清楚爲何要那麼設計,爲何要那麼玩,簡直了,看來這一塊瞭解的仍是不夠透徹,後面把這一塊吃透後,得重寫一遍

最後給出代碼的git地址 : https://github.com/liuyueyi/java-web-archetype/tree/demo

(這個工程主要是一個簡單的java web demo實例工程,會逐漸的向其中添加一些公用的組件(工做中get到什麼,就往裏面塞什麼東西))

相關文章
相關標籤/搜索