天天看點

JAVA 實作 委托事件

遺留問題:當監聽事件的方法 參數不一緻時,會直接報錯,而不是 不執行

如果沒有其他問題,應該是最終版本了。如有發現問題的大神,請留言謝謝

/**
 * @Author 喬占江 qq:59663479
 * @Data 2021/1/24 12:14
 * @Description 如果想 釋出事件 委托,需要實作這接口
 * @Version 1.0
 */
public interface IEvent {
    /**
     * 使用 Event時,實作這個接口,調用Event.init(this);Event會回調 iWantUseEvent 方法
     */
    void iWantUseEvent();

    /**
     * 當 使用Event的對象 被銷毀前,應該通知Event,把自己釋出的 事件删除
     * 這個接口 可以忽視,僅僅是告知作用;
     */
    default void iNotUseEvent() {
        Event.delete(this);
    }
}
           
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author 喬占江 qq:59663479
 * @Data 2021/1/23 20:37
 * @Description 事件委托 實作
 * @Version 3.0  
 */
public class Event {
    // 管理所有的 事件委托;
    // Map<釋出 事件委托 的對象,Map<事件委托 的名字,監聽者 資訊>>
    private static Map<Object, Map<String, EventDefine>> events = new ConcurrentHashMap<>();


    public static void init(IEvent user) {
        //  回調 使用者 
        user.iWantUseEvent();

    }

    /**
     * 釋出一個 事件委托
     *
     * @param promulgator 釋出 事件委托 的對象
     * @param eventName   事件委托 名
     */
    public static void release(Object promulgator, String eventName) {
        Map<String, EventDefine> promulgatorEvent = events.get(promulgator);


        // 沒有這個對象的 事件委托,把這個對象 和本次釋出的 事件委托 加入到 Map中
        if (promulgatorEvent == null) {
            promulgatorEvent = new ConcurrentHashMap<>();
            promulgatorEvent.put(eventName, new EventDefine(eventName));
            events.put(promulgator, promulgatorEvent);
            // System.out.println(promulgator + " 釋出了一個event,eventName為 " + eventName);
        } else {
            // 檢視 是否已釋出 過目前 事件委托,沒釋出過,則加入,釋出過 則跳過
            EventDefine eventDefine = promulgatorEvent.get(eventName);

            if (eventDefine == null) {
                promulgatorEvent.put(eventName, new EventDefine(eventName));
                // System.out.println(promulgator + " 釋出了一個event,eventName為 " + eventName);
            } else {
                System.out.println(promulgator + " 已釋出過 eventName為 " + eventName + " 的 事件委托");
            }
        }

    }

    /**
     * 監聽 某個 事件
     *
     * @param promulgator 釋出這個 事件委托 的對象
     * @param receiver    監聽者
     * @param eventName   監聽的 事件委托 名
     * @param methodName  當事件觸發,通知的方法
     * @return
     */
    public static boolean listen(Object promulgator, Object receiver, String eventName, String methodName) {
        Map<String, EventDefine> promulgatorDefineMap = events.get(promulgator);
        if (promulgatorDefineMap == null) {
            System.out.println("沒有 " + promulgator + " 釋出的 事件委托");
            return false;
        }
        // 擷取這個對象釋出的所有 事件委托
        EventDefine eventDefine = promulgatorDefineMap.get(eventName);
        if (eventDefine == null) {
            System.out.println(promulgator + " 沒有釋出eventName為 " + eventName + " 的事件委托");
            return false;
        }
        // 記錄 事件委托 資訊
        eventDefine.add(receiver, methodName);
        return true;
    }

    /**
     * 不再監聽 一個 事件委托
     *
     * @param promulgator 事件委托 釋出者
     * @param receiver    監聽者
     * @param eventName   事件委托 名字
     */
    public static void remove(Object promulgator, Object receiver, String eventName) {
        Map<String, EventDefine> promulgatorDefineMap = events.get(promulgator);

        if (promulgatorDefineMap == null) {
            System.out.println(promulgator + " 沒有釋出過 事件委托 ");
            return;
        }
        // 擷取這個對象釋出的所有 事件委托
        EventDefine eventDefine = promulgatorDefineMap.get(eventName);
        if (eventDefine == null) {
            System.out.println(promulgator + " 沒有釋出eventName為 " + eventName + " 的 事件委托");
            return;
        }
        eventDefine.remove(receiver);
    }

    /**
     * 不再釋出 關于 eventName 的資訊,将會删除這個 事件委托,以及所有 監聽者資訊
     *
     * @param promulgator 事件釋出者
     * @param eventName   不再釋出的事件的名字
     */
    public static void delete(Object promulgator, String eventName) {
        Map<String, EventDefine> promulgatorDefineMap = events.get(promulgator);
        if (promulgatorDefineMap != null) {
            promulgatorDefineMap.remove(eventName);
            System.err.println(promulgator + "不再釋出eventName為 " + eventName + " 的事件");
        }
    }

    /**
     * 不再釋出任何 事件委托
     *
     * @param promulgator 釋出人
     */
    public static void delete(Object promulgator) {
        if (events.containsKey(promulgator)) {
            events.remove(promulgator);

            System.out.println(promulgator + " 将之前釋出的事件全部移除了");
            System.out.println("還釋出有 事件委托 的對象個數為:" + events.size());
        }
    }

    /**
     * 觸發一個 事件
     *
     * @param promulgator 這個事件是誰 釋出的
     * @param eventName   事件名
     * @param params      傳遞的參數
     */
    public static void invoke(Object promulgator, String eventName, Object... params) {
        Map<String, EventDefine> promulgatorDefineMap = events.get(promulgator);
        if (promulgatorDefineMap == null) {
            System.out.println(promulgator + " 沒有釋出過 事件委托");
            return;
        }
        EventDefine eventDefine = promulgatorDefineMap.get(eventName);
        if (eventDefine == null) {
            System.out.println(promulgator + " 沒有釋出eventName為 " + eventName + " 的 事件委托");
            return;
        }
        eventDefine.send(params);
    }


    /**
     * 當一個 事件委托 釋出後,這個 事件委托 名字,對應一個 EventDefine
     * EventDefine 中 包含所有  監聽這個 事件委托 的  對象和方法名
     */
    private static class EventDefine {
        // 事件委托 名
        private String eventName;

        // Map<監聽 事件委托 的對象,監聽 事件委托 的方法名>
        private Map<Object, String> eventMap = null;

        public EventDefine(String eventName) {
            this.eventName = eventName;
        }

        public void add(Object object, String methodName) {
            if (eventMap == null) {
                eventMap = new ConcurrentHashMap<>();
                eventMap.put(object, methodName);
            } else if (!eventMap.containsKey(object)) {
                eventMap.put(object, methodName);
            }
        }

        public void remove(Object object) {
            eventMap.remove(object);
        }

        /**
         * 通知所有 監聽者
         *
         * @param params
         */
        public void send(Object... params) {
            // 根據要發送的資料,擷取對應的 類型,然後使用反射擷取 對應的方法;
            Class[] classes = paramsToTypes(params);

            try {

                for (Object object : eventMap.keySet()) {
                    // 遇到問題:當參數不比對時,直接抛出了異常;
                    // 期望是,參數不比對傳回null,是否有解決方案呢?
                    Method method = object.getClass().getMethod(eventMap.get(object), classes);
                    if (method != null) {
                        method.invoke(object, params);
                    } else {
                        System.out.println("沒這方法");
                    }
                }

            } catch (Exception e) {
                System.out.println("eventName為:" + eventName + "的調用發生了錯誤");
                e.printStackTrace();
            }
        }

        /**
         * 将參數[] 轉為 類型[] 傳回
         */
        private static Class[] paramsToTypes(Object... params) {
            if (params == null) {
                return null;
            }

            Class[] paramTypes = new Class[params.length];
            for (int i = 0; i < params.length; i++) {
                paramTypes[i] = params[i].getClass();
            }
            return paramTypes;
        }
    }

}


           

下面是配合 測試使用的代碼

import com.card.event.Event;
import com.card.event.IEvent;

/**
 * @Author 喬占江 qq:59663479
 * @Data 2021/1/24 10:29
 * @Description
 * @Version 1.0
 */
public class Player implements IEvent {

    private int hp;
    private int mp;
    private int gold;

    Bag bag = new Bag();


    @Override
    public void iWantUseEvent() {
        Event.release(this, "goldUpdate");
        Event.release(this, "hpUpdate");
        Event.release(this, "mpUpdate");
    }

    public Player() {
        Event.init(this);
        bag.init(this);
    }

    @Override
    public void iNotUseEvent(){
        Event.delete(this);
    }

    /**
     * @param listener
     * @param eventName
     * @param methodName
     */
    public void listenEvent(Object listener, String eventName, String methodName) {
        Event.listen(this, listener, eventName, methodName);
    }


    public int getHp() {
        return hp;
    }

    public void setHp(Integer hp) {
        this.hp = hp;
        Event.invoke(this, "hpUpdate", hp);
    }

    public int getMp() {
        return mp;
    }

    public void setMp(Integer mp) {
        this.mp = mp;
        Event.invoke(this, "mpUpdate", mp);
    }

    public int getGold() {
        return gold;
    }

    public void setGold(Integer gold) {
        this.gold = gold;
        Event.invoke(this, "goldUpdate", gold);
    }


}
           
/**
 * @Author 喬占江 qq:59663479
 * @Data 2021/1/24 10:33
 * @Description
 * @Version 1.0
 */
public class Bag {
    private Player owner;

    public void init(Player player){
        owner = player;
        owner.listenEvent(this,"goldUpdate","onGoldUpdate");
        owner.listenEvent(this,"hpUpdate","onHpUpdate");
        owner.listenEvent(this,"mpUpdate","onMpUpdate");
    }


    /**
     * 當金币發生改變時,會調用這個方法,這裡本來可以不帶參數,可以直接用 owner 擷取
     * 為了做測試 特意加了參數;這個參數必須是 對象類型。不可用是基本資料類型
     */
    public void onGoldUpdate(Integer gold){
        System.out.println("收到gold變化為:" + gold);

    }

    public void onHpUpdate(Integer hp){
        System.out.println("收到hp變化為:" + hp);

    }

    public void onMpUpdate(Integer mp){
        System.out.println("收到mp變化為:" + mp);

    }
    
}
           

測試

public class EventDemo {

    public static void main(String[] args) throws InterruptedException {
        Player player = new Player();
        player.setGold(1000);
        player.setHp(800);
        player.setMp(600);

        player.iNotUseEvent();

    }

}