前言
???? 一直很少動手寫文檔,覺得自己只是一個新手,對很多技術點或者概念理解的不透徹,沒多少自信。二來,做程序遇到困難時,在論壇上找到的幾乎都是通篇的代碼,只能一行行解讀代碼,自己自圓其說。我不太喜歡這種模式,但是又不知道如何找到詳細的資料,天天百度,費力不討好。在圖書館找參考書,大多都是些基礎的東西,也沒什么幫助。
??? 昨天,老師布置了一些作業,問我們應不應該強制?回來后我想了想,還是覺得不該強制,如果你愛JAVA,你自己會花時間在上面的。寫文檔倒是很有必要的。寫文檔,一來可以記錄你的學習歷程,二來可以暴露一些問題,如果你講不清楚一個問題,很有可能時因為你對該問題的理解不透徹。
??
?? 今天,我要敘述的內容是觀察者模式,也稱發布/訂閱模式,監聽器模式
??
觀察者模式是軟體設計模式的一種。在此種模式中,一個目標物件(被觀察者)管理所有相依于它的觀察者物件,并且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實作事件處理系統。
?
???敘述該問題,通篇敘述不如通過實例來的易理解,不用實例,估計我也講不清楚
。
??
?? 例:給一個JFrame上的JTree添加節點,通過線程給JTree添加5個節點
?
1.直接調用模式
?????學習這么久,老師從沒有講過搭建模型,記得在寫五子棋游戲的時候,那時候寫的是郁悶的很,每添加一個功能時,都要修改好幾個類。類與類的耦合非常高。????
??? 實現JFrame的JTree上添加節點:
??? 思路:用線程來模擬給JTree添加節點,在線程的構造器中傳入JTree對象。
?????
/********************** *此類為消息類,用來作為樹節點 */ public class Msg { private String text; public Msg(String text){ this.text = text; } //重寫toString方法,設置節點顯示內容 public String toString(){ return this.text; } }
?
? 線程類:
????
public class TestThread extends Thread{ private JTree tree; public TestThread(JTree tree){ //傳入JTree對象 this.tree = tree ; } public void run(){ DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot(); for(int i=0;i<5;i++){ Msg msg = new Msg("第"+(i+1)+"個節點"); DefaultMutableTreeNode node = new DefaultMutableTreeNode(msg); //添加節點 root.add(node); //刷新樹 javax.swing.SwingUtilities.updateComponentTreeUI(tree); //延時1S try{ Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } } }
?測試界面類:
??
public static void main(String[] args){ JFrame fr = new JFrame("Test_1"); fr.setSize(300, 300); fr.setDefaultCloseOperation(3); fr.setLayout(new FlowLayout(FlowLayout.LEFT)); DefaultMutableTreeNode root = new DefaultMutableTreeNode("根節點"); JTree tree = new JTree(root); fr.add(tree); new TestThread(tree).start(); fr.setVisible(true); }
?
總結1 :軟件是應用戶需要而編寫的,肯定會經常修改。這種模式由于界面類和實現類之間的高耦合,要改界面類和實現
???????類都要改,?不利于程序的修改和擴展。
??????????怎樣才能避免這種牽一發動全身的問題呢?
?????????? 想想,如果將具體對象間的依賴關系轉化為接口實現的抽象關系會怎樣,就可將界面類和實現類分別封裝,使相互
?????? 獨立,各自修改互補影響。對于本例,在線程中添加一個監聽器隊列,當產生事件時通知隊列中的各監聽器即可,這
??????? 需要有添加,移除,通知監聽器的方法。
?
????????? 可將上面的代碼修改一下
觀察者模式
?????首先需要定義消息處理接口
public interface InListener { public void actionPerformed(Msg msg); }
???根據需要編寫實現類,本例中:
?? MyJTreeListener
??
public class MyJTreeListener implements InListener{ private JTree tree; private DefaultMutableTreeNode root; public MyJTreeListener(JTree tree){ this.tree = tree; root = (DefaultMutableTreeNode) tree.getModel().getRoot(); } public void actionPerformed(Msg msg) { DefaultMutableTreeNode node = new DefaultMutableTreeNode(msg); root.add(node); javax.swing.SwingUtilities.updateComponentTreeUI(tree); } }
?線程類:
?
public class TestThread extends Thread{ //監聽器隊列 private ArrayList<InListener> list ; public TestThread(){ //初始化監聽器隊列 list = new ArrayList(); } public void run(){ for(int i=0;i<5;i++){ Msg msg = new Msg("第"+(i+1)+"條消息"); //通知各監聽器 this.fireInListener(msg); //延時1S try{ Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } } } /***********************8 * 通知所有消息監聽器 * @param msg:消息 */ public void fireInListener(Msg msg){ for(int i=0;i<list.size();i++){ InListener listener = list.get(i); listener.actionPerformed(msg); } } /*************************** * 添加事件監聽器 * @param l:要添加的監聽器 * @return:添加結果 */ public boolean addListener(InListener l){ return list.add(l); } /**************************** * 移除事件監聽器 * @param l:要移除的監聽器 * @return:移除結果 */ public boolean removeListener(InListener l){ return list.remove(l); }
?測試:
??
public static void main(String[] args){ JFrame fr = new JFrame("Test_2"); fr.setSize(300, 300); fr.setDefaultCloseOperation(3); fr.setLayout(new FlowLayout(FlowLayout.LEFT)); DefaultMutableTreeNode root = new DefaultMutableTreeNode("根節點"); JTree tree = new JTree(root); fr.add(tree); TestThread nt = new TestThread(); //注冊監聽器 nt.addListener(new MyJTreeListener(tree)); nt.start(); fr.setVisible(true); }
? 總結2: 這樣,線程對象和具體的事件處理對象就可以相互獨立的修改??? 但實際上這是一種具體對抽象的關系,可以進一步將事件產生類(本例中為線程)進行抽象,變為抽象對抽象的關系,畢竟現實中的事件產生可以有很多種情況,往往風馬牛不相及。
?
??? 只需再定義一個事件產生接口即可:
??? InEventSource
???
public interface InEventSource { public ArrayList<InListener> list = new ArrayList(); public boolean addListener(InListener l); public boolean removeListener(InListener l); public void fireInListener(Msg msg); }
?
線程類:
??
public class TestThread extends Thread implements InEventSource{ public void run(){ for(int j=0;j<5;j++){ Msg msg = new Msg("第"+(j+1)+"條消息"); this.fireInListener(msg); //延時1S try{ Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } } } public void fireInListener(Msg msg){ for(int i=0;i<list.size();i++){ InListener listener = list.get(i); listener.actionPerformed(msg); } } public boolean addListener(InListener l){ return list.add(l); } public boolean removeListener(InListener l){ return list.remove(l); }
?
測試不變
?
總結3: 觀察者模式所進行的工作就是解耦,就是將具體的對象之間的關系進行抽象,變為抽象對抽象的關系,降低具體對象間的依賴性,從而實現良好的修改,擴充性。其實,就是讓我們懂得封裝的重要性,教我們如何封裝。
?
?
什么時候應該采用觀察者模式
???? 我覺得,當一個對象改變時,同時要改變其他對象,而且不知道要改變多少其他對象時,應該考慮采用觀察者模式。
?
?
由于我的水平有限,講的不是很清楚,如果有錯的,或是不好的地方,歡迎指出批評。
?
附上 《大話設計模式》下載鏈接 書中第14章有很生動的講解
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
