最近在看Tomcat和Spring的源碼,在啓動的時候註冊了各類Listener,事件觸發的時候就執行,這裏就用到了設計模式中的觀察者模式。html
想一想之前在學Java的GUI編程的時候,就用到了事件的註冊監聽,而後寫了一個小程序試驗一下:
點擊按鈕觸發相應的事件java
public class ButtonTest extends JFrame { ButtonTest() { JPanel panel = new JPanel(); JButton button1 = new JButton("按鈕一"); JButton button2 = new JButton("按鈕二"); panel.add(button1); panel.add(button2); this.getContentPane().add(panel); this.setVisible(true); button1.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("你按了按鈕一"); } }); button2.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("你按了按鈕二"); } }); } public static void main(String args[]) { new ButtonTest(); } }
嗯,寫起來確實很簡單,這是封裝好的,咱們只須要註冊事件,並實現對應的事件響應就好了。
那麼這種神奇的模式是怎麼實現的呢?
如下爲個人分析過程。o(╯□╰)o
首先,咱們來回歸本質,看看到底這種模式方便在哪兒。若是不這麼作的話,咱們須要再每次點擊按鈕的代碼裏面把響應的內容加上,若是有一千個事件響應,咱們須要把這一千個響應都寫到點擊按鈕這個方法裏面,Oh!!MyGod!太麻煩了!
若是忘掉設計模式,咱們想一想這種事情應該怎麼作呢?
咱們須要把這一千個響應管理起來,而後在事件觸發的時候,把這些響應統一調用,不就好了?問題來了...
1.如何管理起來?
2.每一個響應都不同,每次寫響應的時候都須要去點擊事件那裏加一段,改動之前的代碼,此乃編程之大忌,耦合度過高
想一想回答,
1.把每個響應都經過一個響應鏈來管理,觸發事件時,遍歷這個響應鏈,作出響應。
2.能夠定義一個接口,在響應的地方傳入該接口,調用接口的方法。這樣,每次須要響應的時候,實現該接口,傳入具體響應的對象,這樣咱們就只用關心如何響應了,不用關心如何去調用響應的辦法。
暈了暈了暈了……
來看看Java中的事件處理機制是如何實現的吧!編程
來自維基百科的結構圖:
圖看看就行。
場景:按下開關,燈作出的響應。
1.定義事件對象小程序
/** * 事件對象。繼承EventObject * Created by HuangYQ on 2016/5/31. */ public class SwitchEvent extends EventObject { private String switchState; //表示開關的狀態 public SwitchEvent(Switch source, String switchState) { super(source); this.switchState = switchState; } public void setSwitchState(String switchState) { this.switchState = switchState; } public String getSwitchState() { return switchState; } }
2.定義事件監聽接口設計模式
/** * 事件監聽接口 * Created by HuangYQ on 2016/5/31. */ public interface SwitchListener extends EventListener { public void handleEvent(SwitchEvent switchEvent); }
咱們能夠在這裏,再定義事件監聽類,這些類具體實現了監聽功能和事件處理功能。也能夠用下面主程序中的匿名內部類來實現。原理同樣。ide
3.定義事件源對象(具體的事件源,好比說,你點擊一個button,那麼button就是event source,要想使button對某些事件進行響應,你就須要註冊特定的listener。這裏指開關)函數
/** * 電源開關 * 事件源對象,相似於Swing中的button * Created by HuangYQ on 2016/5/31. */ public class Switch { private Vector switchListenerList = new Vector(); public void addListener(Object listener) { switchListenerList.add(listener); } protected void open() { SwitchEvent switchEvent = new SwitchEvent(this, "開"); notifyListeners(switchEvent); } protected void close() { SwitchEvent switchEvent = new SwitchEvent(this, "關"); notifyListeners(switchEvent); } private void notifyListeners(SwitchEvent switchEvent) { Iterator iterator = switchListenerList.iterator(); while (iterator.hasNext()) { SwitchListener switchListener = (SwitchListener) iterator.next(); switchListener.handleEvent(switchEvent); } } }
4.主程序學習
public class SwitchMain { public static void main(String[] args) { Switch switchTest = new Switch(); switchTest.addListener(new SwitchListener() { @Override public void handleEvent(SwitchEvent switchEvent) { //Do what ever you want ! System.out.println(switchEvent.getSwitchState()); } }); //觸發 switchTest.open(); switchTest.close(); } }
運行:ui
開 關
仔細分析下程序,和咱們以前YY的監聽器其實大體同樣,
我的以爲最難理解的就是事件對象SwitchEvent了。
問題
1.爲何要繼承EventObject?不繼承行不行
2.事件和事件源有什麼聯繫,爲何還須要事件對象
答:
1.繼承自EventObject只是爲了規範,不實現固然能夠。在handleEvent的時候,咱們能夠經過事件對象拿到咱們須要的東西,這裏面就有最基本的source,也就是這裏的switch對象了,
2.事件和事件源關係並不大,事件對象裏面包含source罷了,經過構造函數注入。this
[參考]