觀察者模式介紹 觀察者模式是一種非常有用的設計模式,在軟體系統中,當一個對象的行為依賴於另一個對象的狀態時,觀察者模式就非常有用。如果不適用觀察者模式,而實現類似的功能,可能就需要另外啟動一個線程不停地監聽另一個對象的狀態,這樣會得不償失。如果在一個複雜的系統中,可能就需要開啟很多的線程來監聽對象狀
- 觀察者模式介紹
觀察者模式是一種非常有用的設計模式,在軟體系統中,當一個對象的行為依賴於另一個對象的狀態時,觀察者模式就非常有用。如果不適用觀察者模式,而實現類似的功能,可能就需要另外啟動一個線程不停地監聽另一個對象的狀態,這樣會得不償失。如果在一個複雜的系統中,可能就需要開啟很多的線程來監聽對象狀態的變化,這樣會使系統的性能產生額外的負擔。而觀察者模式就可以在單線程下使某一對象及時得知所依賴對象狀態的變化而做出行為。
觀察者模式的經典結構:
其中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.0View 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中實現的觀察者模式,用法簡單功能強大,和我們上面寫的觀察者模式實現原理是一樣的。觀察者模式可以用於事件監聽、通知發佈等場合,可以確保觀察者在不適用輪詢監控的情況下,可以及時得到相關消息和事件。