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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...