博客 / 詳情

返回

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

前言

本文關鍵詞:觀察者模式

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

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

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

引入問題:httpClient

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

在Angular中有這麼一種寫法:

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

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

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

圖片.png

觀察者模式

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

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

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

    /**
     * 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()執行後的數據是不變的,具體怎麼操作這個數據,就要看傳進去的方法了。

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

總結

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

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

user avatar susouth 頭像 gaoming13 頭像 pugongyingxiangyanghua 頭像 201926 頭像 musicfe 頭像 warn 頭像 codeoop 頭像 gfeteam 頭像 beilee 頭像 william_wang_5f4c69a02c77b 頭像 mofaboshi 頭像 xuriliang 頭像
47 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.