觀察者模式實現事件驅動模型(非GUI事件)

先引入四人幫之書裏面關於觀察者模式的一段高度概括

觀察者模式——

定義對象間的一種一對多的依賴關係,當一個對象的狀態發生變化時,所有依賴它的對象都得到通知並自動更新。

事件驅動模型是觀察者模式的一種典型應用。該模式主要由事件源,事件對象,以及事件監聽器三元素構成。以常見的點擊事件爲例子。按鈕爲事件源,相當於觀察者模式中的被觀察者;點擊爲事件對象;事件監聽器,相當於觀察者模式中的觀察者。當事件源的屬性發生變化時,所有監聽該事件的事件監聽器都會接收到消息並作出響應。

關於“事件”這種抽象,最直觀的是在於圖形界面應用裏,如常見的點擊、拖動事件。實際上,世間萬物各種屬性的變化,我們都可以稱爲事件。例如風停了,怪物死亡了等等。

下面以具體代碼作例子,說明事件驅動模型的應用。

假設這樣的業務需求:遊戲服務器希望在玩家升級時觸發多種效果。例如玩家升級後,各種屬性都會提高,開啓新的系統玩法,學習新的技能……如果不採用事件驅動模型,那麼寫出來的代碼可能是這樣——

private void handleRoleUpgrade(Object role){
		if(meetUpgradeCondition(role)){//滿足升級條件
			RoleManager.getInstance().upgradeAttribution(role);//屬性提升
			SkillManager.getInstance().learnNewSkill(role);//學會新技能
			//其他一堆業務
		}
	}
所有相關業務的邏輯都耦合到這裏。當然,這樣做也有好處,最重要的一點是業務邏輯絕對清晰。

如果使用事件驅動模型,那麼在這裏只需分發一個升級事件,那麼該事件的所有監聽器便會自動作出響應。

首先定義事件類(Event.java)

事件類以玩家暱稱代表事件源,將事件源綁定在事件類,是爲了程序方便。

package observer;

public class Event {
	
	private final  String userName;  	 //玩家暱稱
	private final EventType evtType; 	//事件類型
	
	public Event(String userName,EventType evtType){
		this.userName = userName;
		this.evtType = evtType;
	}

	public String getUserName() {
		return userName;
	}

	public EventType getEvtType() {
		return evtType;
	}
	
}
事件類型枚舉(EventType.java)
package observer;

public enum EventType {
	LEVEL_UP,		//角色升級

	;
}

具體事件——升級事件(LevelUpEvent.java)

package observer;

public class LevelUpEvent extends Event{

	public LevelUpEvent(String userName, EventType evtType) {
		super(userName, evtType);
	}

}

事件監聽器接口(EventListener.java)

package observer;

public interface EventListener {

	public void handleEvent(Event event);
	
}
具體事件監聽器——屬性變化監聽器(AttrChangeListener.java)
package observer;

/**
 *  屬性變化監聽器
 */
public class AttrChangeListener implements EventListener{

	@Override
	public void handleEvent(Event event) {
		System.err.println(event.getUserName()+"升級了,攻擊力,防禦力都將大幅提升");
	}

}
具體事件監聽器——學習技能監聽器(SkillListener.java)

package observer;

public class SkillListener implements EventListener{

	@Override
	public void handleEvent(Event event) {
		System.err.println(event.getUserName()+"升級了,學會劍神新技能");
	}

}
事件分發器接口,主要用於事件的註冊及派發(EventDispatcher.java)
package observer;

public interface EventDispatcher {

	/**
	 *  註冊事件
	 */
	public void registerEvent(EventType evtType,EventListener listener);
	
	/**
	 *  派發事件
	 */
	public void fireEvent(Event event);
	
}
具體事件分發器(CommonEventDispatcher.java)
package observer;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

public enum CommonEventDispatcher  implements EventDispatcher{
	
	INSTANCE;	 	//採用枚舉實現單例模式
	
	private final Map<EventType,Set<EventListener>> observers = new HashMap<>();
	
	@Override
	public void registerEvent(EventType evtType, EventListener listener) {
		Set<EventListener> listeners = observers.get(evtType);
		if(listeners == null){
			listeners = new CopyOnWriteArraySet<EventListener>();
			observers.put(evtType, listeners);
		}
		listeners.add(listener);
	}
	
	@Override
	public void fireEvent(Event event) {
		if(event == null){
			throw new NullPointerException("event cannot be null");
		}
		
		EventType evtType = event.getEvtType();
		Set<EventListener> listeners = observers.get(evtType);
		if(listeners != null){
			for(EventListener listener:listeners){
				try{
					listener.handleEvent(event);
				}catch(Exception e){
					e.printStackTrace();  //防止其中一個listener報異常而中斷其他邏輯
				}
			}
		}
	}

}
測試代碼(Test.java)
package observer;

public class Test {

	public static void main(String[] args) {
		CommonEventDispatcher dispatcher = CommonEventDispatcher.INSTANCE;
		EventListener listener1 = new AttrChangeListener();
		EventListener listener2 = new SkillListener();
		
		Event levelUpEvent = new LevelUpEvent("李逍遙",EventType.LEVEL_UP);
		dispatcher.registerEvent(EventType.LEVEL_UP, listener1);
		dispatcher.registerEvent(EventType.LEVEL_UP, listener2);
		
		dispatcher.fireEvent(levelUpEvent);
	}
	
}
程序運行截圖





發佈了48 篇原創文章 · 獲贊 98 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章