阿里P6+面試:介紹下觀察者模式?

訊息佇列(MQ),一種能實現生產者到消費者單向通訊的通訊模型,這也是現在常用的主流中介軟體。常見有 RabbitMQ、ActiveMQ、Kafka等 他們的特點也有很多 比如

解偶

非同步

廣播

削峰

等等多種優勢特點。

在設計模式中也有一種模式能有效的達到

解偶

非同步

的特點,那就是

觀察者模式

又稱為

釋出訂閱模式

今天阿丙就分享一下實際開發中比較常見的這種模式

大綱

阿里P6+面試:介紹下觀察者模式?

定義

什麼是觀察者模式?他的目的是什麼?

當一個物件的狀態發生改變時,已經登記的其他物件能夠觀察到這一改變從而作出自己相對應的改變。透過這種方式來達到減少依賴關係,解耦合的作用。

舉一個例子,就好比微信朋友圈,以當前個人作為訂閱者,好友作為主題。一個人發一條動態朋友圈出去,他的好友都能看到這個朋友圈,並且可以在自主選擇點贊或者評論。

感覺有點抽象,還是看看他有哪些主要角色:

阿里P6+面試:介紹下觀察者模式?

Subject(主題): 主要由類實現的可觀察的介面,通知觀察者使用attach方法,以及取消觀察的detach方法。

ConcreteSubject(具體主題): 是一個實現主題介面的類,處理觀察者的變化

Observe(觀察者): 觀察者是一個由物件水岸的介面,根據主題中的更改而進行更新。

這麼看角色也不多,但是感覺還是有點抽象,我們還是用具體例項程式碼來走一遍吧,我們還是以上面的朋友圈為例看看程式碼實現

public interface Subject {    // 新增訂閱關係    void attach(Observer observer);    // 移除訂閱關係    void detach(Observer observer);    // 通知訂閱者    void notifyObservers(String message);}

先建立一個主題定義,定義新增刪除關係以及通知訂閱者

public class ConcreteSubject implements Subject {    // 訂閱者容器    private List observers = new ArrayList();    @Override    public void attach(Observer observer) {        // 新增訂閱關係        observers。add(observer);    }    @Override    public void detach(Observer observer) {        // 移除訂閱關係        observers。remove(observer);    }    @Override    public void notifyObservers(String message) {        // 通知訂閱者們        for (Observer observer : observers) {            observer。update(message);        }    }}

其次再建立的具體主題,並且構建一個容器來維護訂閱關係,支援新增刪除關係,以及通知訂閱者

public interface Observer {    // 處理業務邏輯    void update(String message);}

建立一個觀察者介面,方便我們管理

public class FriendOneObserver implements Observer {     @Override    public void update(String message) {        // 模擬處理業務邏輯        System。out。println(“FriendOne 知道了你發動態了” + message);    }}

最後就是建立具體的觀察者類,實現觀察者介面的update方法,處理本身的業務邏輯

public class test {        public static void main(String[] args) {        ConcreteSubject subject = new ConcreteSubject();        // 這裡假設是新增好友        subject。attach(new FriendOneObserver());        FriendTwoObserver twoObserver = new FriendTwoObserver();        subject。attach(twoObserver);        // 傳送朋友圈動態        subject。notifyObservers(“第一個朋友圈訊息”);        // 輸出結果: FriendOne 知道了你發動態了第一個朋友圈訊息        //          FriendTwo 知道了你發動態了第一個朋友圈訊息        // 這裡發現 twoObserver 是個推薦賣茶葉的,刪除好友        subject。detach(twoObserver);        subject。notifyObservers(“第二個朋友圈訊息”);        // 輸出結果:FriendOne 知道了你發動態了第二個朋友圈訊息    }}

最後就是看測試結果了,透過ConcreteSubject 維護了一個訂閱關係,在透過notifyObservers 方法通知訂閱者之後,觀察者都獲取到訊息從而處理自己的業務邏輯。

阿里P6+面試:介紹下觀察者模式?

這裡細心的朋友已經達到了解耦合的效果,同時也減少了依賴關係,每個觀察者根本不要知道釋出者處理了什麼業務邏輯,也不用依賴釋出者任何業務模型,只關心自己本身需要處理的邏輯就可以了。

如果有新的業務新增進來,我們也只需要建立一個新的訂閱者,並且維護到observers 容器中即可,也符合我們的開閉原則。

這裡只是一種同步的實現方式,我們還可以擴充套件更多其他的非同步實現方式,或者採用多執行緒等實現方式。

框架應用

觀察者模式在框架的中的應用也是應該很多

第一種 熟悉JDK的人應該知道 在java。util 包下 除了常用的 集合 和map之外還有一個

Observable

類,他的實現方式其實就是觀察者模式。裡面也有

新增、刪除、通知

等方法。

這裡需要注意是的 他是用Vector 作為訂閱關係的容器,同時在他的定義方法中都新增synchronized關鍵字修飾類,以達到執行緒安全的目的

這裡我貼出了關鍵原始碼,感興趣的同學可以自己開啟並且觀看每個方法的註釋。

阿里P6+面試:介紹下觀察者模式?

第二種 在Spring中有一個ApplicationListener,也是採用觀察者模式來處理的,ApplicationEventMulticaster作為主題,裡面有新增,刪除,通知等。

spring有一些內建的事件,當完成某種操作時會發出某些事件動作,他的處理方式也就上面的這種模式,當然這裡面還有很多,我沒有細講,有興趣的同學可以仔細瞭解下Spring的啟動過程。

import java。util。EventListener;/** * Interface to be implemented by application event listeners。 * Based on the standard {@code java。util。EventListener} interface *  for the Observer design pattern。 // 這裡也已經說明是採用觀察者模式 * * 

As of Spring 3。0, an ApplicationListener can generically declare the event type * that it is interested in。 When registered with a Spring ApplicationContext, events * will be filtered accordingly, with the listener getting invoked for matching event * objects only。 * * @author Rod Johnson * @author Juergen Hoeller * @param  the specific ApplicationEvent subclass to listen to * @see org。springframework。context。event。ApplicationEventMulticaster //主題 */@FunctionalInterfacepublic interface ApplicationListener extends EventListener { /**  * Handle an application event。  * @param event the event to respond to  */ void onApplicationEvent(E event);}

第三種 Google Guava的事件處理機制

Guava EventBus

他的實現也是採用設計模式中的觀察者設計模式。

EventBus 當前實現有兩種方式:

EventBus // 同步阻塞模式AsyncEventBus // // 非同步非阻塞模式

EventBus內部也提供來一系列的方法來供我們方便使用:

register 方法作為新增觀察者

unregister方法刪除觀察者

post 方法傳送通知訊息等

使用起來非常方便。新增@Subscribe註解就可以建立一個訂閱者了,具體的使用方式可以看看官網。

現實業務改造舉例

框架應用的例子這麼多,在業務場景中其實也有很多地方可以使用到,這裡我還是給大家舉一個例子。

在新使用者註冊成功之後我們需要給使用者做兩件事情,第一是傳送註冊成功簡訊,第二是給用傳送新人優惠券。

看到這個問題 大家可能首先會想到用MQ訊息處理呀,是的,用訊息確實可以的,但是這裡我們用觀察者模式來實現這個問題,同時可以給大家演示一下,同步或者非同步的問題。

public class SendNewPersonCouponObserver implements Observer {    ExecutorService pool = Executors。newFixedThreadPool(2);    @Override    public void update(String message) {        Future future = pool。submit(new Callable() {            @Override            public String call() throws Exception {                TimeUnit。SECONDS。sleep(3);                // 處理響應的業務邏輯                return “呼叫發券服務,返回結果”;            }        });        try {            // 假設等待200毫秒 沒有獲取到返回值結果則認為失敗            System。out。println(future。get(4000, TimeUnit。MILLISECONDS));        } catch (Exception e) {            // 執行非同步獲取失敗            // 記錄日誌,定時任務重試等        }        // 第一種不關心返回值結果        Thread thread = new Thread(new Runnable() {            @SneakyThrows            @Override            public void run() {                // 模擬服務呼叫 執行緒睡3秒鐘                TimeUnit。SECONDS。sleep(3);                System。out。println(“傳送新人優惠券”);            }        });        thread。start();        System。out。println(“執行非同步返回”);    }}

public class SendSuccessMessageObserver implements Observer {    @Override    public void update(String message) {        // 處理業務邏輯        System。out。println(“註冊成功”);    }    public static void main(String[] args) {        // 假設使用者註冊成功直接通知觀察者,改幹自己的事情了        ConcreteSubject subject = buildSubject();        subject。notifyObservers(“”);    }    private static ConcreteSubject buildSubject() {        ConcreteSubject subject = new ConcreteSubject();        subject。attach(new SendSuccessMessageObserver());        subject。attach(new SendNewPersonCouponObserver());        return subject;    }}

阿里P6+面試:介紹下觀察者模式?

這裡我們新寫了兩個觀察者,主要看第一個SendNewPersonCouponObserver,這裡了非同步開啟新的執行緒去處理我們的業務邏輯,當我們關心返回值的時候可以用Future來獲取返回結果,當不關心的返回值的化,直接開啟普通執行緒就可以了。

這個舉例整體其實還是比較簡單的主要是為了說清楚非同步執行緒處理,當然如果用Guava EventBus也可以實現。而且也不復雜,感興趣的朋友可以自己去試試。

當前現在有更加好的中介軟體

MQ訊息佇列

來處理這個業務問題,使得我們更加從容的面對這類場景問題,但是一些資源不足,不想引入新的系統。還是可以用這種方式來處理問題的。

設計模式學習的不是程式碼,而是學習每種模式的思想,他們分別處理的是什麼業務場景。

總結

大家看完本篇文章不知道有發現沒有,其實整個內容都是圍繞了

解耦

的思想來寫的,觀察者模式作為行為型設計模式,主要也是為了不同的業務行為的程式碼

解耦

合理的使用設計模式可以使程式碼結構更加清晰,同時還能滿足不同的小模組符合單一職責,以及開閉原則,從而達到前面寫工廠模式說的,提高程式碼的可擴充套件性,維護成本低的特點。

敖丙把自己的面試文章整理成了一本電子書,共 1630頁!

乾貨滿滿,字字精髓。目錄如下,還有我複習時總結的面試題以及簡歷模板,現在免費送給大家。

阿里P6+面試:介紹下觀察者模式?

我是敖丙,

你知道的越多,你不知道的越多

,感謝各位人才的:

點贊

收藏

評論

,我們下期見!

回覆【資料】有我準備的一線大廠面試資料和簡歷模板