設計模式九: 觀察者模式(Observer Pattern)

簡介

觀察者屬於行爲型模式的一種, 又叫發佈-訂閱模式. 若是一個對象的狀態發生改變,依賴他的對象都將發生變化, 那麼這種狀況就適合使用觀察者模式.html

它包含兩個術語,主題(Subject),觀察者(Observer), 主題管理一個觀察者的列表, 並在狀態發生變化時通知到他們.java

實現層面上, 主題定義了一個觀察者列表並能夠管理這個列表(將觀察者註冊進去和撤銷註冊的行爲), 同時定義了通知到全部觀察者的行爲.api

Java類庫有默認的觀察者實現類Observer. 使用觀察者模式的還有事件監聽, RxJava等.多線程

意圖

定義對象之間的一對多依賴,當一個對象狀態改變時,它的全部依賴都會收到通知而且自動更新狀態。oracle

類圖

實現

一. 定義抽象觀察者異步

/**
 * 觀察者抽象角色
 */
public interface MyObserver {
    void update(Subject subject);
}

二. 定義抽象主題this

/**
 * 主題抽象角色
 */
public abstract class Subject {
    private ArrayList<MyObserver> list = new ArrayList<MyObserver>();

    public Subject register(MyObserver observer){
        if(!list.contains(observer)){
            list.add(observer);
        }
        return this;
    }

    public Subject unRegister(MyObserver observer){
        if(list.contains(observer)){
            list.remove(observer);
        }
        return this;
    }

    public final void notifyObservers(){
        for (MyObserver item:list) {
            item.update(this);
        }
    }

    /**
     * 具體狀態的變動
     */
    abstract void change();
}

三. 定義具體主題spa

/**
 * 具體主題角色
 */
@Data
public class ConcreteSubject extends Subject {
    private String name;
    public void change() {
        this.setName("王多魚");
        super.notifyObservers();
    }
}

四. 定義觀察者的實現, 分別定義兩個不一樣實現線程

public class UncleObserver implements MyObserver {
    public void update(Subject subject) {
        ConcreteSubject concreteSubject = (ConcreteSubject)subject;
        System.out.println("UncleObserver 知道了: "+concreteSubject.getName());
    }
}

public class HentaiObserver implements MyObserver {
    public void update(Subject subject) {
        ConcreteSubject concreteSubject = (ConcreteSubject)subject;
        System.out.println("HentaiObserver 知道了: "+concreteSubject.getName());
    }
}

五. 調用, 首先註冊觀察者,而後調用主題的變動方法code

// 實例化主題後爲其註冊了兩個觀察者的實例, 並最終調用主題的變動方法觸發通知行爲
public static void main(String[] args) {
    new ConcreteSubject()
            .register(new UncleObserver())
            .register(new HentaiObserver())
            .change();
}

總結

關鍵點: 定義通知到觀察者的方法要注意, 若是有耗時嚴重的會阻塞其餘觀察者獲得通知, 能夠異步多線程實現; 避免循環引用;

觀察者分推模式和拉模式, 推模式是指將主題的變動信息發送給觀察者, 觀察者能夠用, 也能夠不用. 拉模式則是將主題的引用發送給觀察者, 觀察者經過引用根據本身須要獲取所需信息. 示例中使用的就是這種模式.

代碼修改成推模式

// 觀察者的方法接收具體信息
void update(String name);

// 主題的通知方法作相應修改
public final void notifyObservers(){
    for (MyObserver item:list) {
        item.update("王多魚");
    }
}
相關文章
相關標籤/搜索