Cuuube / blog

blog on Mirror
1 stars 0 forks source link

[javascript][设计模式]用观察者模式实现ajax框架 #62

Open Cuuube opened 6 years ago

Cuuube commented 6 years ago

用观察者模式实现ajax框架

简介

之前研究了观察者模式,观察者模式是个很好的一对多的消息通知范例。

被观察者只需负责更新自己的状态。而观察者施加一个观察,然后设置一个相应行动就可以了。

这种情形在做ajax请求的时候大有可为。

在ajax中,xhr对象一次发生一次询问,根据服务器相应,会更新自身状态。这就是一个很好的观察者模式的应用。很明显的,xhr充当的是被观察者。我们需要的是一些观察者,观察到xhr状态变化后执行操作。

(xhr本身提供了事件驱动的接口。我们通过一些代码,将这些事件驱动的设计变成观察者模式的设计很容易。同理可应用于nodejs的事件驱动设计的对象。提一下这一块用于读者的举一反三。)

代码

首先需要之前写的观察者基类。我就把基类和集成的类放在一起了。用代码注释来分割。

// 观察者模式的ajax请求

// ------- 基类 ---------
// 被观察者
class Subject {
    constructor () {
        this.observerList = [];
        this.m_state = '';
    }

    // 被观察者注册一个观察者
    register (observer) {
        this.observerList.push(observer);
        console.log('添加一个观察者!');
    }

    // 删除观察者
    unregister (observer) {
        if (this.observerList.indexOf(observer) > -1) {
            this.observerList.splice(this.observerList.indexOf(observer), 1);
            console.log('删除一个观察者!');
        }
    }

    // 被观察者把状态更新广播给所有观察者
    boardcast (value) {
        this.observerList.forEach((observer) => {
            observer.action(value);
        })
    }

    get state () {
        return this.m_state;
    }

    // 被改变状态状态改变。调用广播告诉所有观察者
    set state (value) {
        this.m_state = value;
        this.boardcast(this.state);
    }
}

// 观察者
class Observer {
    constructor (subject) {
        this.subject = subject;
    }

    // 观察者主动观察被观察者。允许传入被观察者的数组
    observe (subjects, callback) {
        if (!(subjects instanceof Array)) {
            subjects = [subjects];
        }
        subjects.forEach((subject) => {
            subject.register(this);
            if (callback) {
                this.action = callback;
            }
        })
    }

    // 动作方法
    action (value) {
        new Error('请勿直接调用抽象类的方法!');
    }
}

// ------- 继承 start ---------
// 被观察者,核心为一个xhr。
class XHRSubject extends Subject {
    constructor (method, url, data, headers) {
        super();
        // 注意,此xhr用作只用于示范。功能很贫弱。请读者自行增强ajax功能
        this.xhr = new XMLHttpRequest();
        this.state = '';
        this.xhr.open(method, url);
        // xhr接收到响应后更新状态
        this.xhr.onload = (res) => {
            this.state = res
        };
        this.xhr.onerror = (err) => {
            this.state = err;
        };
        // xhr即时发送请求。不想即时请求也可以写一个方法来发送请求,手动或者其他时机调用。
        this.xhr.send();
    }
}

// 观察者,这里写一个动作方法。或者不在类里声明动作,在实例化对象的时候增加动作方法
class Watcher extends Observer {
    action (res) {
        let data = res.currentTarget.response;
        console.log('资料在观察者类定义中处理');
    }
}

// ----------- use --------------

function main() {
    // 实例化xhr被观察者
    let xhrSub = new XHRSubject('GET', '/');
    let xhrSub2 = new XHRSubject('GET', '/123');

    // 实例化观察者
    let watcher1 = new Watcher();
    let watcher2 = new Watcher();

    // “被观察者主动注册观察者”式调用
    xhrSub.register(watcher1);

    // 观察者主动调用
    watcher2.observe(xhrSub2, (res) => {
        let data = res.currentTarget.response;
        console.log('资料处理成功。资料处理方法在观察者订阅时赋予');
    });
}

main();

结果输出

添加一个观察者!
添加一个观察者!
资料在观察者类定义中处理
GET https://www.google.com.sg/123 404 ()
资料处理成功。资料处理方法在观察者订阅时赋予

上面代码可以直接在浏览器中运行,观察。

因为用到XHRHttpRequest类,所以不能在nodejs环境跑了。。。

因为只试着用观察者模式实现了ajax的使用。重点在于这种设计模式。所以请各位自行扩展ajax的功能啦~

最后。。。你听说过rxjs么?