設計模式 —— 觀察者模式

簡介

觀察者模式(Observer Pattern)屬於行爲型模式的一種,它定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態變化時,會通知全部的觀察者對象,使他們可以自動更新。觀察者模式的別名包括髮布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。java

觀察者模式的推拉方式

  • 推方式:

主題對象向觀察者推送主題的詳細信息,無論觀察者是否須要,推送的信息一般是主題對象的所有或部分數據。設計模式

  • 拉方式:

主題對象在通知觀察者的時候,只傳遞少許信息。若是觀察者須要更具體的信息,由觀察者主動到主題對象中獲取,至關因而觀察者從主題對象中拉數據。通常這種模型的實現中,會把主題對象自身經過update()方法傳遞給觀察者,這樣在觀察者須要獲取數據的時候,就能夠經過這個引用來獲取了。異步

實例

觀察者模式所涉及的角色有:ide

  1. 抽象主題角色(Subject):抽象主題角色把全部對觀察者對象的引用保存在一個彙集(好比ArrayList對象)裏,每一個主題均可以有任何數量的觀察者。抽象主題提供一個接口,能夠增長和刪除觀察者對象,抽象主題角色又叫作抽象被觀察者(Observable)角色;this

  2. 具體主題角色(ConcreteSubject):將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給全部登記過的觀察者發出通知。具體主題角色又叫作具體被觀察者(Concrete Observable)角色;spa

  3. 抽象觀察者角色(Observer):爲全部的具體觀察者定義一個接口,在獲得主題的通知時更新本身,這個接口叫作更新接口;設計

  4. 具體觀察者角色(ConcreteObserver):存儲與主題的狀態自恰的狀態。具體觀察者角色實現抽象觀察者角色所要求的更新接口,以便使自己的狀態與主題的狀態像協調。若是須要,具體觀察者角色能夠保持一個指向具體主題對象的引用。code

  • 推方式:
// 抽象觀察者角色類
public interface Observer {
    void update(String message);
}
// 抽象主題角色類
public abstract class AbstractSubject {
    private List<Observer> list = new ArrayList<>();

    public void attach(Observer observer) {
        list.add(observer);
    }

    public void detach(Observer observer) {
        list.remove(observer);
    }

    public void notifyObserver(String message) {
        for (Observer observer : list) {
            observer.update(message);
        }
    }
}

// 具體觀察者角色類
public class WxUserObserver implements Observer {

    private String name;

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

    @Override
    public void update(String message) {
        System.out.println(name + "收到推送消息:" + message);
    }

}

// 具體主題角色類
public class WxSubject extends AbstractSubject {

    public void change(String message) {
        this.notifyObserver(message);
    }
}

// 客戶端
public class Client {
    @Test
    public void test() {
        WxSubject subject = new WxSubject();

        final WxUserObserver zs = new WxUserObserver("張三");
        final WxUserObserver ls = new WxUserObserver("李四");
        final WxUserObserver ww = new WxUserObserver("王五");

        // 初始化三個關注用戶
        subject.attach(zs);
        subject.attach(ls);
        subject.attach(ww);

        subject.change("開始推送消息1");
        // 張三取消關注
        subject.detach(zs);
        subject.change("開始推送消息2");
    }
}
複製代碼
張三收到推送消息:開始推送消息1
李四收到推送消息:開始推送消息1
王五收到推送消息:開始推送消息1
李四收到推送消息:開始推送消息2
王五收到推送消息:開始推送消息2
複製代碼
  • 拉方式:
// 抽象觀察者角色類
public interface Observer {
    void update(AbstractSubject subject);
}

// 抽象主題角色類
public abstract class AbstractSubject {
    private List<Observer> list = new ArrayList<>();

    public void attach(Observer observer) {
        list.add(observer);
    }

    public void detach(Observer observer) {
        list.remove(observer);
    }

    public void notifyObserver() {
        for (Observer observer : list) {
            observer.update(this);
        }
    }
}

// 具體觀察者角色類
public class WxUserObserver implements Observer {

    private String name;

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

    @Override
    public void update(String message) {
        System.out.println(name + "收到推送消息:" + message);
    }

}

// 具體主題角色類
public class WxSubject extends AbstractSubject {

    private String state;


    public String getState() {
        return state;
    }


    public void change(String state) {
        this.state = state;
        this.notifyObserver();
    }
}

// 客戶端
public class Client {
    @Test
    public void test() {
        WxSubject subject = new WxSubject();

        final WxUserObserver zs = new WxUserObserver("張三");
        final WxUserObserver ls = new WxUserObserver("李四");
        final WxUserObserver ww = new WxUserObserver("王五");

        // 初始化三個關注用戶
        subject.attach(zs);
        subject.attach(ls);
        subject.attach(ww);
        subject.change("State");

        // 李四取消關注
        subject.detach(ls);
        subject.change("new State");
    }
}
複製代碼
張三狀態更新爲:State
李四狀態更新爲:State
王五狀態更新爲:State
張三狀態更新爲:new State
王五狀態更新爲:new State
複製代碼
  • JDK 實現:

Java語言的java.util庫裏面,提供了一個Observable類以及一個Observer接口,構成了Java語言對觀察者模式的支持。 Observer接口只定義了一個update()方法,當被觀察者對象的狀態發生變化時,被觀察者對象的notifyObservers()方法就會調用這一方法。 Observable類是被觀察者類的基類。java.util.Observable提供公開的方法支持觀察者對象,這些方法中有兩個對Observable的子類很是重要:一個是setChanged(),另外一個是notifyObservers()。第一方法setChanged()被調用以後會設置一個內部標記變量,表明被觀察者對象的狀態發生了變化。第二個是notifyObservers(),這個方法被調用時,會調用全部登記過的觀察者對象的update()方法,使這些觀察者對象能夠更新本身。cdn

// 觀察者
public class WxUserObserver implements Observer {

    private String name;

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

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(name + "狀態更新爲:" + ((WxObservable) o).getState());
    }
}

// 被觀察者
public class WxObservable extends Observable {

    private String state;


    public String getState() {
        return state;
    }

    public void change(String state) {
        this.state = state;
        this.setChanged(); // 設置一個內部標記變量
        this.notifyObservers(); // 調用全部登記過的觀察者對象的update()方法
    }
}

public class Client {
    @Test
    public void test() {
        WxObservable observable = new WxObservable();

        final WxUserObserver zs = new WxUserObserver("張三");
        final WxUserObserver ls = new WxUserObserver("李四");
        final WxUserObserver ww = new WxUserObserver("王五");

        observable.addObserver(zs);
        observable.addObserver(ls);
        observable.addObserver(ww);
        observable.change("State");

        observable.deleteObserver(ls);
        observable.change("new State");
    }
}
複製代碼
張三狀態更新爲:State
李四狀態更新爲:State
王五狀態更新爲:State
張三狀態更新爲:new State
王五狀態更新爲:new State
複製代碼

用JDK提供的這種方式能很好方便的實現,推方式和拉方式的設計模式。server

類圖

推方式:

推方式

拉方式:

拉方式

JDK實現:

JDK實現

優勢

  1. 觀察者和被觀察者是抽象耦合的;
  2. 創建一套觸發機制。

缺點

  1. 若是一個被觀察者對象有不少的直接和間接的觀察者的話,將全部的觀察者都通知到會花費不少時間;

  2. 若是在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰;

  3. 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

適用場景

  1. 一個抽象模型有兩個方面,其中一個方面依賴於另外一個方面。將這些方面封裝在獨立的對象中使它們能夠各自獨立地改變和複用;

  2. 一個對象的改變將致使其餘一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,能夠下降對象之間的耦合度;

  3. 一個對象必須通知其餘對象,而並不知道這些對象是誰;

  4. 須要在系統中建立一個觸發鏈,A對象的行爲將影響B對象,B對象的行爲將影響C對象……,可使用觀察者模式建立一種鏈式觸發機制。

總結

觀察者模式用於創建一種對象與對象之間的依賴關係,一個對象發生改變時將自動通知其餘對象,其餘對象將相應做出反應。在 Java 中已經對觀察者模式的支持類 java.util.Observerjava.util.Observable,不須要咱們本身實現。使用中注意避免循環引用。若是順序執行,某一觀察者錯誤會致使系統卡殼,通常採用異步方式。

相關文章
相關標籤/搜索