一、Observer模式的意圖:
在對象的內部狀態發生變化時,自動通知外部對象進行響應。
二、Observer模式的構成:
·被觀察者:內部狀態有可能被改變,而且又需要通知外部的對象
·觀察者:需要對內部狀態的改變做出響應的對象
三、Observer模式的Java實現:
Java的API中已經爲我們提供了Observer模式的實現。具體由java.util.Observable類和java.util.Observer接口完成。
前者有兩個重要的方法:
·setChanged:設置內部狀態爲已改變
·notifyObservers(Object obj):通知觀察者所發生的改變,參數obj是一些改變的信息
後者有一個核心方法:
·update(Object obj):相應被觀察者的改變,其中obj就是被觀察者傳遞過來的信息,該方法會在notifyObservers被調用時自動調用。
下面是Observer模式的實現過程:
·創建一個被觀察者,繼承java.util.Observable
·創建一個觀察者,實現java.util.Observer接口
· 註冊觀察着,調用addObserver(Observer observer)
·在被觀察者改變對象內部狀態的地方,調用setChanged()方法,然後調用notifyObservers(Object)方法,通知被觀察者
·在觀察者的update(Object)方法中,對改變做出響應。
四、Observer模式的好處:
1.Observer模式的優點:
·被觀察者只需要知道誰在觀察它,無需知道具體的觀察細節
·被觀察者一旦發生變化,只需要通過廣播的方式告知觀察者,至於消息如何到達則不需知道。這樣的話無疑消除了被觀察者和觀察者之間通信的硬編碼
·當一個被觀察者同時被多個觀察着觀察時,觀察者可以只選擇自己感興趣的事件,而忽略其它的事件
·多個觀察者組合起來可以形成一個觀察鏈,如果一旦需要回滾多個操作,此時觀察鏈可以發揮作用
·觀察者可以實時對被觀察對象的變化做出響應,例如自動告警、中斷運行等
2.運用Observer模式可以
·屏蔽線程間的通信機制:例如兩個線程之間,主線程可以作爲觀察者,執行線程是被觀察者。彼此之間只知道對方存在,但不知道之間通信的細節
·消除硬編碼:如果沒有Observer模式,則只能採用回調的模式,或者在代碼中顯示地調用觀察者
·優化異常機制:特別適合在異常發生時向頂層監控,減少try-catch代碼量
代碼:
- public class Observable {
- private boolean changed = false;
- private Vector obs;
- //創建被觀察者時就創建一個它持有的觀察者列表,注意,這個列表是需要同步的。
- public Observable() {
- obs = new Vector();
- }
- /**
- * 添加觀察者到觀察者列表中去
- */
- public synchronized void addObserver(Observer o) {
- if (o == null)
- throw new NullPointerException();
- if (!obs.contains(o)) {
- obs.addElement(o);
- }
- }
- /**
- * 刪除一個觀察者
- */
- public synchronized void deleteObserver(Observer o) {
- obs.removeElement(o);
- }
- /**
- * 通知操作,即被觀察者發生變化,通知對應的觀察者進行事先設定的操作,不傳參數的通知方法
- */
- public void notifyObservers() {
- notifyObservers(null);
- }
- /**
- * 與上面的那個通知方法不同的是,這個方法接受一個參數,這個參數一直傳到觀察者裏,以供觀察者使用
- */
- public void notifyObservers(Object arg) {
- Object[] arrLocal;
- synchronized (this) {
- if (!changed)
- return;
- arrLocal = obs.toArray();
- clearChanged();
- }
- for (int i = arrLocal.length-1; i>=0; i--)
- ((Observer)arrLocal[i]).update(this, arg);
- }
- }
- public interface Observer {
- /**
- * This method is called whenever the observed object is changed. An
- * application calls an <tt>Observable</tt> object's
- * <code>notifyObservers</code> method to have all the object's
- * observers notified of the change.
- *
- * @param o the observable object.
- * @param arg an argument passed to the <code>notifyObservers</code>
- * method.
- */
- void update(Observable o, Object arg);
- }
- }
- public class MailObserver implements Observer{
- /**
- * 這個類取名爲MailObserver,顧名思義,她是一個用來發送郵件的觀察者
- */
- public void update(Observable o, Object arg) {
- System.out.println("發送郵件的觀察者已經被執行");
- }
- }
- public class JMSObserver implements Observer{
- public void update(Observable o, Object arg) {
- System.out.println("發送消息給jms服務器的觀察者已經被執行");
- }
- }
- public class Subject extends Observable{
- /**
- * 業務方法,一旦執行某個操作,則通知觀察者
- */
- public void doBusiness(){
- if (true) {
- super.setChanged();
- }
- notifyObservers("現在還沒有的參數");
- }
- public static void main(String [] args) {
- //創建一個被觀察者
- Subject subject = new Subject();
- //創建兩個觀察者
- Observer mailObserver = new MailObserver();
- Observer jmsObserver = new JMSObserver();
- //把兩個觀察者加到被觀察者列表中
- subject.addObserver(mailObserver);
- subject.addObserver(jmsObserver);
- //執行業務操作
- subject.doBusiness();
- }
- }
在spring中使用觀察者模式的方法如下
<bean id="mailObserver" class="MailObserver"/>
<bean id="jmsObserver" class="JMSObserver"/>
<bean id="subjectTarget" class="Subject"/>
<bean id="subject"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject"><ref local="subjectTarget"/></property>
<property name="targetMethod"><value>addObserver</value></property>
<property name="arguments">
<list>
<ref bean="mailObserver"/>
<ref bean="jmsObserver"/>
</list>
</property>
</bean>
觀察者模式的效果有以下幾個優點:
(1)觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體現察者聚集,每一個具體現察者都符合一個抽象觀察者 的接口。被觀察者並不認識任何一個具體觀察者,它只知道它們都有一個共同的接口。由於被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬於不同的抽象 化層次。
(2)觀察者模式支持廣播通信。被觀察者會向所有的登記過的觀察者發出通知。
觀察者模式有下面的一些缺點:
(1)如果一個被觀察者對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
(2)如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰。在使用觀察考模式時要特別注意這一點。
(3)如果對觀察者的通知是通過另外的線程進行異步投遞的話,系統必須保證投遞是以自恰的方式進行的。
(4)雖然觀察者模式可以隨時使觀察者知道所觀察的對象發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎麼發生變化的