理解觀察者模式——用Angular的httpClient來解釋觀察者

前言

本文關鍵詞:觀察者模式segmentfault

觀察者模式是什麼?
觀察者模式和回調函數有什麼關係?
使用httpClient時,加上.subscribe有什麼做用?
Angular的httpClient中如何體現觀察者模式?設計模式

解決了上述問題以後,就寫了這篇文章。函數

(這篇文章其實是給上一篇文章填個坑...上一篇寫到回調函數,卻沒有給出實際應用的例子。)學習

引入問題:httpClient

httpClient是Angular中的一個內置類,用於向後臺發起Http請求、返回請求結果。用它來舉例子是由於功能比較簡單,易於理解。this

在Angular中有這麼一種寫法:url

// 向8080端口的helloWorld路徑發起請求
httpClient.get('http://localhost:8080/helloWorld')
  .subscribe((data) => {
    console.log('請求成功');
    console.log(data);
  }, error);
}

不經意一看,這不就是簡單的鏈式調用麼?——前一個方法返回一個對象,再調用這個對象的方法,再返回對象,再調用方法...
可是仔細看,才發現:這根本就不是一個過程,而是兩個過程啊!
圖片.png
從使用.get向後臺發起請求以後,到調用.subscribe以前,這之中經歷了一個後臺接收數據、處理數據、返回數據的過程。spa

那麼問題來了:挖掘機技術哪家強?前臺如何知道數據何時返回?怎麼在數據返回以後,自動執行後面的代碼來打印返回數據?設計

圖片.png

觀察者模式

觀察者模式,顧名思義,有這樣一個對象,在始終被另外一個對象觀察着、注視着、牢牢的盯着。
用雜誌社作比喻:有一個雜誌社雜誌社裏有一個訂閱報刊的部門,一個讀者向這個部門訂閱了雜誌,今後之後讀者日日期盼着讀到本身買的雜誌,而訂閱部門也會在新的雜誌出版以後,第一時間送到讀者手裏。
這就是觀察者模式,它由兩部分組成:
數據源 + 訂閱者 = 觀察者模式
圖片.png
數據源和訂閱者之間是一對多的關係。訂閱者經過某種方法,向數據源發起訂閱,此後,數據源一旦發生變動,會立刻通知全部的訂閱者。code

既然知道了原理,那麼在httpClient中具體是怎麼實現的呢?
咱們找到源碼,httpClient類的全部方法都寫在裏面,而且有一大堆重載:
圖片.pngserver

咱們拿出一個方法來看看:

/**
     * Constructs a `GET` request that interprets the body as a text string
     * and returns the response as a string value.
     *
     * @param url     The endpoint URL.
     * @param options The HTTP options to send with the request.
     *
     * @return An `Observable` of the response, with the response body of type string.
     */
    get(url: string, options: {
        headers?: HttpHeaders | {
            [header: string]: string | string[];
        };
        observe?: 'body';
        params?: HttpParams | {
            [param: string]: string | string[];
        };
        reportProgress?: boolean;
        responseType: 'text';
        withCredentials?: boolean;
    }): Observable<string>;

把代碼格式化一下,變成咱們容易理解的方式:
圖片.png

能夠看到get方法的必填參數是請求地址URL,還有可選參數。但這些都不重要,關鍵在於,普通的方法,用方法名+參數就完事了,好比:

test();

再看這個方法的最後,多出了一個: Observable<string>這是觀察者的關鍵!這條代碼的意思是:返回類型爲「觀察者」的對象,這個觀察者攜帶着string類型的被觀察的數據。

Observable是「可觀察的」意思,聲明一個方法有可觀察的對象以後,這個方法的返回值就再也不是一個普通變量,而是一個「觀察者」對象,咱們對這個對象使用.subscribe方法訂閱,就能夠傳入函數進行回調了。
咱們在控制檯打印一下.get()方法的返回值:

console.log(this.httpClient.get(`http://localhost:8080/Klass/${klass.id}`));

圖片.png
果真是一個對象,這個對象包含了訂閱數據源的功能,等到數據返回以後再使用.subscribe方法來操做返回的數據。
圖片.png

接下來,咱們來看subscribe方法:

subscribe(observer?: PartialObserver<T>): Subscription;
/** @deprecated Use an observer instead of a complete callback */
subscribe(next: null | undefined, error: null | undefined, complete: () => void): Subscription;
/** @deprecated Use an observer instead of an error callback */
subscribe(next: null | undefined, error: (error: any) => void, complete?: () => void): Subscription;
/** @deprecated Use an observer instead of a complete callback */
subscribe(next: (value: T) => void, error: null | undefined, complete: () => void): Subscription;
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;

依然是選出一個,格式化成熟悉的形式:
圖片.png

能夠看出,這個方法須要傳入三個回調函數,分別爲:

  • HTTP請求執行成功後執行什麼方法
  • HTTP請求失敗後執行什麼方法
  • 不管成功或失敗,最後執行什麼方法
    圖片.png

因此只要把對應的方法傳入,就能夠了(也能夠不都傳入,subscribe因爲有重載函數,能夠處理不一樣的參數)。

觀察者和回調函數有什麼關係?

在調用.subscribe時,爲何要傳入success和error兩個方法?

——實際上是爲了代碼解耦,回調函數的目的原本就是爲了代碼解耦。在方法A()中傳入方法B()用於回調,就能夠把方法A()執行以後的數據交給方法B()來操做。因此方法A()執行後的數據是不變的,具體怎麼操做這個數據,就要看傳進去的方法了。

同理,觀察者向數據源發起訂閱以後,當數據源發生變化時,把新的數據通知給訂閱者。數據既然已經拿到手,怎麼處理數據就是訂閱者的事了,和數據源不要緊了。因此在觀察者模式裏使用回調函數的好處在於:當處理返回值的功能發生變化時,並不用改動數據源的任何代碼。
這就比如:雜誌社把雜誌交給客戶以後,客戶想不想看、何時看,或者想把雜誌扔掉,都是客戶本身的事情,和雜誌社沒有半毛錢的關係。

總結

數據源 + 訂閱者 = 觀察者模式
觀察者模式,是設計模式裏面最簡單的,也是最好理解的一種。

筆者也處於初學階段,之後會學到更多的設計模式。對於學習的過程來講,最大的喜悅,無非就是那種豁然開朗的感受了,從一開始的一團漿糊到後來的融會貫通。這種喜悅,應該就是學習最大的回報吧。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息