java設計優化--觀察者模式

来源:http://www.cnblogs.com/lcngu/archive/2016/03/07/5251859.html
-Advertisement-
Play Games

觀察者模式介紹 觀察者模式是一種非常有用的設計模式,在軟體系統中,當一個對象的行為依賴於另一個對象的狀態時,觀察者模式就非常有用。如果不適用觀察者模式,而實現類似的功能,可能就需要另外啟動一個線程不停地監聽另一個對象的狀態,這樣會得不償失。如果在一個複雜的系統中,可能就需要開啟很多的線程來監聽對象狀


 

  • 觀察者模式介紹

  觀察者模式是一種非常有用的設計模式,在軟體系統中,當一個對象的行為依賴於另一個對象的狀態時,觀察者模式就非常有用。如果不適用觀察者模式,而實現類似的功能,可能就需要另外啟動一個線程不停地監聽另一個對象的狀態,這樣會得不償失。如果在一個複雜的系統中,可能就需要開啟很多的線程來監聽對象狀態的變化,這樣會使系統的性能產生額外的負擔。而觀察者模式就可以在單線程下使某一對象及時得知所依賴對象狀態的變化而做出行為。

  觀察者模式的經典結構:

  

   其中ISubject是觀察對象(被觀察者對象),它維持著一個觀察者對象列表,可以增加或刪除觀察者。IObserver是觀察者,它依賴於ISubject對象狀態的變化而做出行為。當ISubject對象的狀態發生變化時,它可以通過inform()方法通知觀察者。

  觀察者模式的主要角色功能如下圖:

  

  • 觀察者實例

  現在簡單實現一個觀察者的小例子。

  主題介面:

1 public interface ISubject {
2     void attach(IObserver observer);
3     void detach(IObserver observer);
4     void inform();
5 }

  觀察者介面:

1 public interface IObserver {
2     void update(Event event);
3 }

  事件(對應現實中的點擊等事件也可以理解為上文中說到的狀態變化):

1 public class Event {
2 
3 }

  具體的主題實現:

 1 public class ConcreteSubject implements ISubject {
 2     Vector<IObserver> obversers = new Vector<IObserver>();//觀察者隊列
 3     @Override
 4     public void attach(IObserver observer) {
 5         obversers.add(observer);
 6     }
 7 
 8     @Override
 9     public void detach(IObserver observer) {
10         obversers.remove(observer);
11     }
12 
13     @Override
14     public void inform() {
15         Event event = new Event();
16         for(IObserver obverser:obversers){
17             obverser.update(event);
18         }
19     }
20 
21 }

  具體的觀察者:

1 public class ConcreteObserver implements IObserver {
2 
3     @Override
4     public void update(Event event) {
5         System.out.println("ConcreteObserver.update()");
6     }
7 
8 }

  測試代碼:

public class Test {
    public static void main(String[] args) {
        IObserver observer1 = new ConcreteObserver();
        IObserver observer2 = new ConcreteObserver();
        ISubject subject = new ConcreteSubject();
        subject.attach(observer1);
        subject.attach(observer2);
        subject.inform();
    }
}

  可以看出,通過被觀察者狀態變化而調用某一方法使觀察者收到通知而做出反應,通過委托降低了代碼的耦合度。

  觀察者模式十分常用,以致於JDK內部就為開發人員準備了一套觀察者模式的實現。在java.util包中,就包括了Obserable類和Observer介面。在Observable中就實現了觀察對象的主要功能,如:添加觀察者、刪除觀察者和通知觀察者等。Observer是觀察者介面,它的update方法會在Obserable類的notifyObservers()中被回調以獲得最新的狀態變化。

  以現在比較火熱的購房作為觀察者模式的一個例子:現在很多購房者關註房價的變化,每當房價發生變動的時候,購房者就會收到通知。這樣購房者就是觀察者,他們關註著房子的價格。

  觀察對象房子代碼:

 1 public class House extends Observable{
 2     private float price;
 3     
 4     public House(float price) {
 5         super();
 6         this.price = price;
 7     }
 8 
 9     public float getPrice() {
10         return price;
11     }
12 
13     public void setPrice(float price) {
14         super.setChanged();//設置變化點
15         super.notifyObservers(price);//價格變動
16         this.price = price;
17     }
18 
19     @Override
20     public String toString() {
21         // TODO Auto-generated method stub
22         return "房子的價格為:"+this.price;
23     }
24 }

  觀察者購房者代碼:

 1 public class CustomerObserver implements Observer{
 2     
 3     private String name;
 4     
 5     public CustomerObserver(String name) {
 6         super();
 7         this.name = name;
 8     }
 9 
10     @Override
11     public void update(Observable o, Object arg) {
12         if (arg instanceof Float) {
13             System.out.println(this.name+"觀察到房子價格變動為:"+arg);
14         }
15     }
16 }

  測試代碼:

 1 public class Test1 {
 2     public static void main(String[] args) {
 3         House h = new House(1000000) ;  
 4         CustomerObserver hpo1 = new CustomerObserver("購房者A") ;  
 5         CustomerObserver hpo2 = new CustomerObserver("購房者B") ;  
 6         CustomerObserver hpo3 = new CustomerObserver("購房者C") ;  
 7         h.addObserver(hpo1) ;  
 8         h.addObserver(hpo2) ;  
 9         h.addObserver(hpo3) ;  
10         System.out.println(h) ; // 輸出房子價格  
11         h.setPrice(666666) ;    // 修改房子價格  
12         System.out.println(h) ; // 輸出房子價格 
13     }
14 }

  輸出結果為:

1 房子價格為:1000000.0
2 購房者C觀察到價格更改為:666666.0
3 購房者B觀察到價格更改為:666666.0
4 購房者A觀察到價格更改為:666666.0
5 房子價格為:666666.0
View Code

  Observer源碼:

 1 public class Observable {
 2     private boolean changed = false;//判斷觀察對象是否發生變化
 3     private Vector obs;//觀察者隊列
 4     public Observable() {
 5         obs = new Vector();
 6     }
 7 
 8   
 9     public synchronized void addObserver(Observer o) {
10         if (o == null)
11             throw new NullPointerException();
12         if (!obs.contains(o)) {
13             obs.addElement(o);
14         }
15     }
16 
17     public synchronized void deleteObserver(Observer o) {
18         obs.removeElement(o);
19     }
20 
21     
22     public void notifyObservers() {
23         notifyObservers(null);
24     }
25 
26   //通知觀察者
27     public void notifyObservers(Object arg) {
28        
29         Object[] arrLocal;
30 
31         synchronized (this) {
32            
33             if (!changed)
34                 return;
35             arrLocal = obs.toArray();
36             clearChanged();
37         }
38 
39         for (int i = arrLocal.length-1; i>=0; i--)
40             ((Observer)arrLocal[i]).update(this, arg);
41     }
42 
43   
44     public synchronized void deleteObservers() {
45         obs.removeAllElements();
46     }
47 
48   
49     protected synchronized void setChanged() {
50         changed = true;
51     }
52 
53  
54     protected synchronized void clearChanged() {
55         changed = false;
56     }
57 
58     
59     public synchronized boolean hasChanged() {
60         return changed;
61     }
62 
63   
64     public synchronized int countObservers() {
65         return obs.size();
66     }
67 }
View Code

  可以發現JDK中實現的觀察者模式,用法簡單功能強大,和我們上面寫的觀察者模式實現原理是一樣的。觀察者模式可以用於事件監聽、通知發佈等場合,可以確保觀察者在不適用輪詢監控的情況下,可以及時得到相關消息和事件。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在[Elixir001]中使用 mix escript.build 生成一個lifelog 的escript啟動腳本。 今天我們嘗試一下另一種方式:生成Archives。 我們先添加一個Task 1. 查看下我們現在有那一些task. 這個命令非常有用,:) > mix help mix # Run
  • 我們寫程式,難免會有問題(是經常會遇到問題 ),而PHP遇到錯誤時,就會給出出錯腳本的位置、行數和原因。有很多人說,這並沒有什麼大不了。確實,在調試程式階段,這確實是沒啥的,而且我認為給出錯誤路徑是必要的。 但泄露了實際路徑的後果是不堪設想的,對於某些入侵者,這個信息可是非常重要,而事實上現在有很多
  • 一、文字版本: bin: 該目錄下存放的是二進位可執行文件,如果是安裝版,那麼這個目錄下會有兩個exe文件:tomcat6.exe、tomcat6w.exe,前者是在控制臺下啟動Tomcat,後者是彈出UGI視窗啟動Tomcat;如果是解壓版,那麼會有startup.bat和shutdown.bat
  • 這篇文章首先走馬觀花瞭解一下程式啟動那一刻都做了些什麼 1 Program StarOfficeMain; 2 3 uses 4 StarOfficeApplication, 5 MainForm in 'Form\MainForm.pas' {frmMain}, 6 StarMainFormInt
  • 最近打算清理掉之前的博客。寫一些關於twisted的框架的文章,twisted是python優秀,成熟,廣泛使用的非同步網路框架;打算從網路基礎開始寫起,到例子,到源碼分析,爭取對twisted有個比較清晰的認識,自己也能再次從中有所收穫。最近比較忙,有空就更新。這是第一篇,想到哪裡寫哪裡,最後再整理
  • 轉載自 https://springframework.guru/mocking-unit-tests-mockito/
  • 在C++的類中,都會有一個或多個構造函數、一個析構函數、一個賦值運算操作符。即使我們自己定義的類中,沒有顯示定義它們,編譯器也會聲明一個預設構造函數、一個析構函數和一個賦值運算操作符。例如: 1 //聲明一個空類 2 class Empty{}; 3 4 //但是這個空類和下麵這個類是等同的 5 c
  • SEVERE: The required Server component failed to start so Tomcat is unable to start. org.apache.catalina.LifecycleException: Failed to start component
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...