理解观察者模式——用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()执行后的数据是不变的,具体怎么操做这个数据,就要看传进去的方法了。

同理,观察者向数据源发起订阅以后,当数据源发生变化时,把新的数据通知给订阅者。数据既然已经拿到手,怎么处理数据就是订阅者的事了,和数据源不要紧了。因此在观察者模式里使用回调函数的好处在于:当处理返回值的功能发生变化时,并不用改动数据源的任何代码。
这就比如:杂志社把杂志交给客户以后,客户想不想看、何时看,或者想把杂志扔掉,都是客户本身的事情,和杂志社没有半毛钱的关系。

总结

数据源 + 订阅者 = 观察者模式
观察者模式,是设计模式里面最简单的,也是最好理解的一种。

笔者也处于初学阶段,之后会学到更多的设计模式。对于学习的过程来讲,最大的喜悦,无非就是那种豁然开朗的感受了,从一开始的一团浆糊到后来的融会贯通。这种喜悦,应该就是学习最大的回报吧。

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息