一、觀察者模式
觀察者模式定義了對象間的一種一對多依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。它將觀察者和被觀察者的對象分離開。提高了應用程式的可維護性和重用性。觀察者模式又稱為發布/訂閱(Publish/Subscribe)模式。
觀察者模式的應用情境:
1、 對一個對象狀態的更新,需要其他對象同步更新,而且其他對象的數量動態可變。
2、 對象僅需要將自己的更新通知給其他對象而不需要知道其他對象的細節。
觀察者模式的優點:
1、 Subject和Observer之間是松耦合的,分別可以各自獨立改變。
2、 Subject在發送廣播通知的時候,無須指定具體的Observer,Observer可以自己決定是否要訂閱Subject的通知。
3、 遵守大部分GRASP原則和常用設計原則,高內聚、低偶合。
觀察者模式的缺陷:
1、 松偶合導致代碼關係不明顯,有時可能難以理解。
2、 如果一個Subject被大量Observer訂閱的話,在廣播通知的時候可能會有效率問題。 二、觀察者模式的實現
實現觀察者模式有很多形式,一種是“註冊---通知---撤銷註冊”的形式。
1. 觀察者Observer:所有潛在的觀察者必須實現觀察者介面,這個介面只有update方法,當主題改變時,它被調用。
public interface Observer{ public void update(float temprature);}
2. 具體觀察者ConcreteObserver: 具體觀察者可以是任何實現了Observer介面的類。觀察者必須註冊具體主題,一邊接收更新。
public class ConcreteObserver implements Observer{ private float temperature; private final Subject subject; public ConcreteObserver(final Subject subject) { this.subject = subject; this.subject.registerObserver(this); } public float getTemperature() { return temperature; } public void setTemperature(final float temperature) { this.temperature = temperature; } @Override public void update(final float temperature) { this.temperature = temperature; }}
3. 可觀察者Subject: 主題介面,即可觀察者Observable,對象使用此介面註冊為觀察者,或者把自己從觀察者中刪除,每個主題可以有多個觀察者。
public interface Subject{ public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers();}
4. 具體可觀察者ConcreteSubject: 一個具體主題實現了主題介面,除了註冊和撤銷之外,具體主題還實現了notifyObservers()方法,這個方法用來在主題狀態改變時更新所有觀察者。具體主題也可能有設定和擷取狀態的方法。
public class ConcreteSubject implements Subject{ private final List<Observer> observers; private float temperature; public float getTemperature() { return temperature; } private void temperatureChanged() { this.notifyObservers(); } public void setTemperature(final float temperature) { this.temperature = temperature; this.temperatureChanged(); } public ConcreteSubject() { observers = new ArrayList<Observer>(); } @Override public void registerObserver(final Observer o) { observers.add(o); } @Override public void removeObserver(final Observer o) { if (observers.indexOf(o) >= 0) { observers.remove(o); } } @Override public void notifyObservers() { for (final Observer o : observers) { o.update(temperature); } }}
用戶端使用代碼如下:
public class Client{ public static void main(final String[] args) { final ConcreteSubject sb = new ConcreteSubject(); sb.setTemperature((float) 20.00); final Observer o = new ConcreteObserver(sb); sb.setTemperature((float) 21.00); }}
三、JDK Observable
在java.util包中包含有基本的Observer介面和Observable抽象類別。功能上和Subject介面和Observer介面類似.不過在使用上,就方便多了,因為許多功能比如說註冊,刪除,通知觀察者的那些功能已經內建好了。Swing編程中的JButton等常用控制項均使用觀察者模式實現。
Button的處理代碼:
JButton btn = new JButton(); btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub //此處做事件處理 } });Button的添加代碼:
public static void main(String[] args) {JFrame p = new JFrame();JButton btn = new JButton("Click Me");btn.addActionListener(new BtnListener());p.add(btn);p.pack();p.setVisible(true);}
四、推和拉
“推”的方式是指,Subject維護一份觀察者的列表,每當有更新發生,Subject會把更新訊息主動推送到各個Observer去。
“拉”的方式是指,各個Observer維護各自所關心的Subject列表,自行決定在合適的時間去Subject擷取相應的更新資料。
“推”的好處包括:
1、高效。如果沒有更新發生,不會有任何更新訊息推送的動作,即每次訊息推送都發生在確確實實的更新事件之後,都是有意義的。
2、即時。事件發生後的第一時間即可觸發通知操作。
3、可以由Subject確立通知的時間,可以避開一些繁忙時間。
4、可以表達出不同事件發生的先後順序。
“拉”的好處包括:
1、如果觀察者眾多,Subject來維護訂閱者的列表,可能困難,或者臃腫,把訂閱關係解脫到Observer去完成。
2、Observer可以不理會它不關心的變更事件,只需要去擷取自己感興趣的事件即可。
3、Observer可以自行決定擷取更新事件的時間。
4、拉的形式可以讓Subject更好地控制各個Observer每次查詢更新的存取權限。