【趣味設計模式系列】之【觀察者模式】

1. 簡介

觀察者模式(Observer Pattern),也叫作發佈訂閱模式(Publish-Subscribe),它定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並被自動更新。java

2. 圖解

水果店進口水果銷量良好,特別是進口蛇果,目前還沒到貨,有幾個女孩小美、小靜、小陶想預約進口蛇果,在到貨以前就給水果老闆說過,等到貨後電話或者微信第一時間通知她們。
數組

3. 案例實現

類圖以下
安全

  • 定義預約顧客爲觀察者接口Observer,當收到被觀察者通知後,執行update()方法;
  • 定義水果店水果爲被觀察者抽象類FruitObservable,持有一個類型爲Vector<Observer>的屬性,存放觀察者集合,同時定義三個方法addObserver()deleteObserver()notifyObservers(),分別爲添加預約顧客,刪除預約顧客,通知已預訂顧客;
  • 定義FruitObservable抽象接口的實現類爲進口蛇果ImportedRedDeliciousApple
  • 定義Observer接口的實現類爲預約進口蛇果的客戶類CustomerObserver

代碼實現以下微信

package com.wzj.observer.example1;

/**
 * @Author: wzj
 * @Date: 2019/10/3 14:53
 * @Desc: 觀察者接口類
 */
public interface Observer {
    //收到通知後觀察者更新
    void update();
}
package com.wzj.observer.example1;

import java.util.Vector;

/**
 * @Author: wzj
 * @Date: 2019/10/3 14:50
 * @Desc: 水果店被觀察者類
 */
public abstract class FruitObservable {

    //線程安全的容器類
    protected Vector<Observer> obs = new Vector<>();

    //添加預約顧客
    public void addObserver(Observer o) {
        obs.addElement(o);
    }

    //刪除固定顧客
    public void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    //通知已預約顧客
    public abstract void notifyObservers();
}
package com.wzj.observer.example1;

/**
 * @Author: wzj
 * @Date: 2019/10/3 15:19
 * @Desc: 進口蛇果類
 */
public class ImportedRedDeliciousApple extends FruitObservable {

    @Override
    public void notifyObservers() {
        System.out.println("通知: 進口蛇果已到...");
        for(Observer o : obs) {
            //通知每位預約顧客
            o.update();
        }
    }
}
package com.wzj.observer.example1;

/**
 * @Author: wzj
 * @Date: 2019/10/3 15:25
 * @Desc: 預約進口蛇果的客戶類
 */
public class CustomerObserver implements Observer {

    //觀察者姓名
    private String name;


    public CustomerObserver(String name){
        this.name = name;
    }

    @Override
    public void update() {
        System.out.println(name + "已收到通知, 將立馬過來購買!");
    }
}

客戶端測試類多線程

package com.wzj.observer.example1;

/**
 * @Author: wzj
 * @Date: 2019/10/3 15:38
 * @Desc:
 */
public class Client {
    public static void main(String[] args) {
        //進口蛇果
        ImportedRedDeliciousApple apple = new ImportedRedDeliciousApple();

        //初始化預約客戶類
        CustomerObserver mei = new CustomerObserver("小美");
        CustomerObserver jing = new CustomerObserver("小靜");
        CustomerObserver tao = new CustomerObserver("小陶");

        //將小美、小靜、小陶加入水果店的預約客戶列表中
        apple.addObserver(mei);
        apple.addObserver(jing);
        apple.addObserver(tao);

        //通知客戶
        apple.notifyObservers();
    }

}

執行結果以下app

通知: 進口蛇果已到...
小美已收到通知, 將立馬過來購買!
小靜已收到通知, 將立馬過來購買!
小陶已收到通知, 將立馬過來購買!

3. JDK中自帶實現與源碼分析

類圖以下
異步


  • JDK自帶接口Observer,當收到被觀察者通知後,執行update()方法;
  • JDK自帶接口Observableboolean類型的屬性changed表示狀態是否變化,持有一個類型爲Vector<Observer>的屬性obs
  • 定義Observable抽象接口的實現類爲進口蛇果ImportedRedDeliciousApple
  • 定義Observer接口的實現類爲預約進口蛇果的客戶類CustomerObserver

代碼實現以下ide

package com.wzj.observer.example2;


import java.util.Observable;
import java.util.Observer;

/**
 * @Author: wzj
 * @Date: 2019/10/3 17:09
 * @Desc: 預約客戶類,繼承觀察者接口
 */
public class CustomerObserver implements Observer {

    private String name;

    public CustomerObserver(String name){
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(name + "已收到通知, 將立馬過來購買!");
    }
}
package com.wzj.observer.example2;

import java.util.Observable;
import java.util.Observer;

/**
 * @Author: wzj
 * @Date: 2019/10/3 17:02
 * @Desc: 進口蛇果類,繼承jdk的Observable接口
 */
public class ImportedRedDeliciousApple extends Observable {


    public void peform() {
        System.out.println("通知: 進口蛇果已到...");
        //設置狀態改變
        this.setChanged();
        this.notifyObservers();
    }

}

客戶端代碼以下源碼分析

package com.wzj.observer.example2;


/**
 * @Author: wzj
 * @Date: 2019/10/3 15:38
 * @Desc: 使用jdk自帶的Observable類和Observer接口
 */
public class Client {
    public static void main(String[] args) {
        //進口蛇果
        ImportedRedDeliciousApple apple = new ImportedRedDeliciousApple();

        //初始化預約客戶類
        CustomerObserver mei = new CustomerObserver("小美");
        CustomerObserver jing = new CustomerObserver("小靜");
        CustomerObserver tao = new CustomerObserver("小陶");

        //將小美、小靜、小陶加入水果店的預約客戶列表中
        apple.addObserver(mei);
        apple.addObserver(jing);
        apple.addObserver(tao);

        //通知客戶
        apple.peform();
    }

}

執行結果測試

通知: 進口蛇果已到...
小陶已收到通知, 將立馬過來購買!
小靜已收到通知, 將立馬過來購買!
小美已收到通知, 將立馬過來購買!

源碼分析

/** 被觀察者 */
public class Observable {
    private boolean changed = false;
    private Vector<Observer> 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);
    }

    

    /** 狀態改變 */
    protected synchronized void setChanged() {
        changed = true;
    }

4. 觀察者模式總結

優勢

  • 觀察者和被觀察者之間是抽象耦合。

    無論是增長觀察者仍是被觀察者都很是容易擴展,並且在Java中都已經實現的抽象層級的定義,因此儘可能使用JDK自帶的接口。
  • 與責任鏈結合使用

    在不少系統中,Observer模式每每和責任鏈共同負責對於事件的處理,其中的某一個observer負責是否將事件進一步傳遞。

缺點

觀察者模式須要考慮一下開發效率和運行效率問題,一個被觀察者,多個觀察者,開發和調試就會比較複雜,並且在Java中消息的通知默認是順序執行,一個觀察者卡殼,會影響總體的執行效率。在這種狀況下,通常考慮採用異步的方式。
相關文章
相關標籤/搜索