設計模式之觀察者模式

關注公衆號JavaStorm 獲取最新文章。java

觀察者模式(有時又被稱爲模型(Model)-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理全部相依於它的觀察者物件,而且在它自己的狀態改變時主動發出通知。這一般透過呼叫各觀察者所提供的方法來實現。此種模式一般被用來實現事件處理系統。git

源代碼地址:https://github.com/UniqueDong/zero-design-stugithub

主要角色

  • 主題接口 Subject:管理全部的觀察者以及數據變化後通知觀察者。
  • 觀察者接口 Observer:接受本身訂閱的主題發佈的數據。
  • 主題實現類。
  • 觀察者實現類。

使用場景

  • 報社的業務就是出版報紙,客戶訂閱該報社,那麼只要有新的報紙出版就會給訂閱報社的客戶送來,只要一直是報社的訂閱客戶,就能一直收到新報紙。編程

  • 當你不想訂閱,取消就能夠,就不會再收到通知。設計模式

  • 報社提供訂閱與取消訂閱的入口。ide

實際上這裏就是一個觀察者模式的例子,報社充當 Subject 主題角色,訂閱報社的客戶就是 Observer 觀察者角色。出版者-主題,訂閱者-觀察者。測試

代碼實現

實現一

首先咱們定義 Subject 主題角色報社 NewspaperSubject。主要提供 註冊觀察者、刪除觀察者、通知全部觀察者方法。this

定義包報紙對象 Newspaperspa

public class Newspaper implements Serializable {
    private LocalDateTime reportTime;
    private String data;

    public LocalDateTime getReportTime() {
        return reportTime;
    }

    public void setReportTime(LocalDateTime reportTime) {
        this.reportTime = reportTime;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Newspaper{" +
                "reportTime=" + reportTime +
                ", data='" + data + '\'' +
                '}';
    }
}

定義主題對象設計

public interface NewspaperSubject {
    /**
     * 註冊觀察者
     * @param observer
     */
    void registerObserver(Observer observer);

    /**
     * 移除觀察者
     * @param observer
     */
    void removeObserver(Observer observer);

    /**
     * 通知全部觀察者
     * @param data
     */
    void notifyObservers(Newspaper data);
}

同時實現該主題,代碼以下:

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ChinaNewspaperSubject implements NewspaperSubject {
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private List<Observer> observers;

    public ChinaNewspaperSubject() {
        this.observers = new ArrayList<>();
    }

    public void setChange() {
        Newspaper newspaper = new Newspaper();
        newspaper.setReportTime(LocalDateTime.now());
        newspaper.setData("發佈新聞");
        notifyObservers(newspaper);
    }

    @Override
    public void registerObserver(Observer observer) {
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        try {
            writeLock.lock();
            observers.add(observer);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeObserver(Observer observer) {
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        try {
            writeLock.lock();
            observers.remove(observer);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void notifyObservers(Newspaper data) {
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        try {
            readLock.lock();
            observers.forEach(item -> item.notice(data));
        } finally {
            readLock.unlock();
        }
    }
}

而後定義觀察者(Observer)角色 也就是報紙訂閱者

public interface Observer {
    /**
     * 接收主題發佈的更新通知
     */
    void notice(Newspaper data);
}

定義觀察者具體實現類:一個香港用戶訂閱報紙

public class HonKongObserver implements Observer {

    @Override
    public void notice(Newspaper data) {
        System.out.println("我收到報社的報紙了:" + "內容是" + data.toString());
    }
}

最後測試

public class Test {
    public static void main(String[] args) {
        //建立報社
        ChinaNewspaperSubject newspaperSubject = new ChinaNewspaperSubject();
        //建立訂閱者
        HonKongObserver honKongObserver = new HonKongObserver();
        //訂閱者關注該報社
        newspaperSubject.registerObserver(honKongObserver);
        //報社發佈新報紙。全部逇訂閱者收到報紙
        newspaperSubject.setChange();

    }
}

方式二:經過JDK內置的實現

咱們的JDK內部與爲咱們實現了觀察者模式。只不過咱們的主題須要繼承 jdk 中的主題,觀察者實現對應的Observer 接口。以前咱們說過要多用組合與委託。面向接口編程而不是實現。內置的主題咱們必須繼承,若想更靈活其實咱們本身定義主題接口會更好,而且也並不難。

首先咱們的主題要先繼承 Observerble ,這是jdk內置的。

public class NumsObservable extends Observable {
    public final static Integer ODD = 1;
    public final static Integer EVEN = 2;

    private int data = 0;

    /**
     * 獲取對象數據
     *
     * @return
     */
    public int getData() {
        return data;
    }

    /**
     * 設置數據變化
     * 根據數據的變化設置相應的標誌變量,通知給訂閱者
     *
     * @param data
     */
    public void setData(int data) {
        this.data = data;
        Integer flag = EVEN;
        if ((this.data & 0x0001) == 1) {
            flag = ODD;
        }
        setChanged();
        // 將變化的變化的標識變量通知給訂閱者
        notifyObservers(flag);
    }
}

接着定義咱們的觀察者:分別是偶數與奇數訂閱者。

/**
 * 奇數內容訂閱類
 * Created by jianqing.li on 2017/6/8.
 */
public class OddObserver implements Observer {
    /**
     * 繼承自Observer接口類,update的方法的實現
     *
     * @param o   主題對象
     * @param arg notifyObservers(flag);傳來的參數,便是標識變量
     */
    @Override
    public void update(Observable o, Object arg) {
        if (arg == NumsObservable.ODD) {
            NumsObservable numsObservable = (NumsObservable) o;
            System.out.println("Data has changed to ODD number " +             numsObservable.getData());
        }
    }
}
/**
 * 偶數內容訂閱類:訂閱主題的內容的偶數變化
 * Created by jianqing.li on 2017/6/8.
 */
public class EvenObserver implements Observer {

    /**
     * 繼承自Observer接口類,update的方法的實現
     *
     * @param o   主題對象
     * @param arg notifyObservers(flag);傳來的參數,便是標識變量
     */
    @Override
    public void update(Observable o, Object arg) {
        if (arg == NumsObservable.EVEN) {
            NumsObservable numsObservable = (NumsObservable) o;
            System.out.println("Data has changed to EVEN number " + numsObservable.getData());
        }
    }
}

編寫測試

public class ObserverTest {
    public static void main(String[] args) {
        // 建立主題
        NumsObservable numsObservable = new NumsObservable();
        //建立訂閱者
        OddObserver oddObserver = new OddObserver();
        EvenObserver evenObserver = new EvenObserver();
        numsObservable.addObserver(oddObserver);
        numsObservable.addObserver(evenObserver);

        //修改主題內容,觸發notifyObservers
        numsObservable.setData(11);
        numsObservable.setData(12);
        numsObservable.setData(13);

    }
}

總結

  • java.util.Observable 的陰暗面:它是一個類,咱們的主題必須繼承它,咱們如果想繼承其餘類就無能爲力了。畢竟 Java 不能多重繼承。
  • 在哪裏有觀察者模式的運用?嘿嘿谷歌的 Guava 類庫中的 EventBus 事件總線使用的就是觀察者模式。
  • Spring 中的 事件傳播 也是如此。
  • 主要做用就是爲交互對象之間的鬆耦合。當一個對象改變,依賴它的對象都會收到通知。主題並不知道觀察者的細節,只知道觀察者實現了 Observer 接口。

客官以爲不過能夠訂閱 公衆號 JavaStorm 與點贊,你的訂閱就是最好的觀察者應用。

相關文章
相關標籤/搜索