1. 程式人生 > >一篇文章帶您搞懂觀察者模式

一篇文章帶您搞懂觀察者模式

定義

  觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件,這個主題物件在狀態發生變化時,會通知所有的觀察者物件,使他們能夠自動地更新自己。

類圖表示

參與者

  根據依賴倒置原則可知,我們希望模組與模組之間不是直接耦合到一起,而是依賴於抽象,所以觀察者模式抽象出了Subject和Observer。這裡的參與者分為4種物件:   1. Subject(主題或者叫通知者):抽象主題,狀態發生改變時,會通知所有與之關聯的物件。同時提供操作關聯物件的方法attach、datach(增加和刪除)   2. Observer(觀察者):抽象的觀察者介面,為所有的具體觀察者定義介面,在主題發生改變的時候更新自己   3. ConcreteSubject(具體主題):主題的具體實現,管理觀察者物件,在主題發生改變的時候,通知觀察者   4. ConcreteObserver(具體觀察者):觀察者的具體實現   通過參與者,我們可以看出這樣設計的好處,無論是具體主題還是具體觀察者,都不會直接呼叫對方。而是呼叫統一的介面,這樣維護、擴充套件和重用都比較方便,並且由於耦合的雙方都只依賴於抽象,而不依賴於具體,所以無論對於具體主題還是具體觀察者它們都不需要知道彼此的具體實現,各自的變化都不會影響到另一邊。這也符合迪米特原則。(但是抽象主題和抽象觀察者還是彼此依賴的)

例項

  這裡給出的例項源於《大話設計模式》中觀察者模式一章。背景是在一家公司中,老闆會經常不在公司,所以球迷小張和股迷小趙就賄賂了前臺的小姐姐,每次老闆到公司視察的時候,前臺小姐姐就會通知小張和小趙老闆來了,它們就會放下手中的事情,繼續工作。   這裡的前臺小姐姐,就是具體主題,狀態就是老闆來了,小張和小趙就是具體觀察者,分析出他們之間的關係,接下來我們就可以寫出觀察者模式的程式碼了。

程式碼實現:

/**
 * 主題.
 *
 * @author jialin.li
 * @date 2019-12-26 14:53
 */
public interface Subject {
    /**
     * 增加一個觀察者
     */
    void attach(Observer observer);
    /**
     * 刪除一個觀察者
     */
    void detach(Observer observer);
    /**
     * 通知
     */
    void notifyObserver();
    /**
     * 表示當前狀態
     */
    String getStatus();
    void setStatus(String status);
}
/**
 * 觀察者
 *
 * @author jialin.li
 * @date 2019-12-26 14:54
 */
public abstract class Observer {
    String name;
    Subject subject;

    public Observer(String name, Subject subject) {
        this.name = name;
        this.subject = subject;
    }

    public abstract void update();
}
import java.util.ArrayList;
import java.util.List;

/**
 * 前臺.
 *
 * @author jialin.li
 * @date 2019-12-26 15:01
 */
public class Receptionist implements Subject {

    private String status;
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObserver() {
        for (Observer observer : observers) {
            observer.update();
        }
    }

    @Override
    public String getStatus() {
        return status;
    }

    @Override
    public void setStatus(String status) {
        this.status = status;
    }
}
/**
 * 球迷.
 *
 * @author jialin.li
 * @date 2019-12-26 15:05
 */
public class NBAObserver extends Observer{

    public NBAObserver(String name, Subject subject) {
        super(name, subject);
    }

    @Override
    public void update() {
        System.out.println(name+": 關閉了NBA球賽,繼續工作");
    }
}
/**
 * 股迷
 *
 * @author jialin.li
 * @date 2019-12-26 15:04
 */
public class StockObserver extends Observer{

    public StockObserver(String name, Subject subject) {
        super(name, subject);
    }

    @Override
    public void update() {
        System.out.println(name+": 關閉了股票行情,繼續工作");
    }
}
/**
 * 測試方法
 *
 * @author jialin.li
 * @date 2019-12-26 14:53
 */
public class Main {
    public static void main(String[] args) {
        Receptionist xiaojiejie = new Receptionist();

        NBAObserver xiaozhang = new NBAObserver("小張", xiaojiejie);
        StockObserver xiaozhao = new StockObserver("小趙", xiaojiejie);

        xiaojiejie.attach(xiaozhang);
        xiaojiejie.attach(xiaozhao);

        xiaojiejie.setStatus("老闆來了");
        System.out.println(xiaojiejie.getStatus());

        xiaojiejie.notifyObserver();
    }
}
一個觀察者模式的就編寫完成了,執行一下吧
老闆來了
小張: 關閉了NBA球賽,繼續工作
小趙: 關閉了股票行情,繼續工作

觀察者模式等於釋出-訂閱模式?

  不等於,在釋出-訂閱模式中,訊息的傳送方,叫做釋出者(publishers),訊息的接收方叫做訂閱者(Subscriber),傳送者是不會直接向訂閱者傳送資訊的,而是通過一個broker(也有資料叫做eventChannel)進行訊息的轉發。下圖總結了這兩個模式的主要差別

總結起來就是:

  1.觀察者模式中,主題和觀察者是可以感受到對方存在的,主題會對觀察者進行管理。而在釋出-訂閱模式中,釋出者和訂閱者不知道對方的存在,他們通過訊息代理進行通訊。   2.觀察者模式中,主題和觀察者實現了鬆耦合,而釋出-訂閱模式,釋出者和訂閱者是完全解耦合的。   3.觀察者模式大多數時候是同步的,比如當事件觸發,Subject就會去呼叫觀察者的方法。而釋出-訂閱模式大多數時候是非同步的(使用訊息佇列)。   4.觀察者 模式需要在單個應用程式地址空間中實現,而釋出-訂閱更像交叉應用模式。

Tomcat中的觀察者模式:

  Tomcat中的容器分為engine、host、context、wrapper ,容器之間是父子關係,通過組合模式將這些容器組合起來,每個容器都具有統一的生命狀態以及生命週期方法。   父容器的init()、start()等方法會呼叫子容器的方法(組合模式)。int()、start()等方法的呼叫,是因為其父容器狀態變化觸發的,所以在Tomcat中元件的生命週期被定義成一個個的狀態(LifecycleState),而狀態的轉變則是一種事件,容器會對每一種的事件設定監聽器,在監聽器中實現一些邏輯,並且對監聽器進行管理(增加或刪除),這就是一種典型的觀察者模式. 觀察者模式相關的方法:
public interface Lifecycle {
    public LifecycleState getState();
    public LifecycleListener[] findLifecycleListeners();
    public void addLifecycleListener(LifecycleListener listener);
    public void removeLifecycleListener(LifecycleListener listener);
}