遺留問題:當監聽事件的方法 參數不一緻時,會直接報錯,而不是 不執行
如果沒有其他問題,應該是最終版本了。如有發現問題的大神,請留言謝謝
/**
* @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();
}
}