前端 JS 设计模式之代理模式、外观模式、观察者模式 和迭代器模式

    技术2022-07-11  76

    一、代理模式

    代理模式,使用者无权访问目标对象,中间加代理,通过代理做授权和控制。对于代理模式的理解,可以举一个例。比如如果广告商想找到明星进行演出,不会直接联系明星,而是联系明星的经纪人,通过经纪人,去与明星之间进行沟通。对于代理模式的应用,在 ES6 中的 Proxy 中也比较常见。代理模式的设计原则验证,代理类和目标类分离,隔离开目标类和使用者,符合开放封闭原则。代理模式与适配器模式的比较:适配器模式是提供一个不同的接口,比如不同版本的插头。代理模式是提供一个一模一样的接口。代理模式与装饰器模式的比较:装饰器模式是扩展功能,原有功能不变且可直接使用。代理模式是显示原有功能,但是经过限制或者阉割之后的。

    代理模式的代码,如下所示:

    // 明星 let star = { name: '张XX', age: 25, phone: '13910733521' } // 经纪人 let agent = new Proxy(star, { get: function (target, key) { if (key === 'phone') { // 返回经纪人自己的手机号 return '18611112222' } if (key === 'price') { // 明星不报价,经纪人报价 return 120000 } return target[key] }, set: function (target, key, val) { if (key === 'customPrice') { if (val < 100000) { // 最低 10w throw new Error('价格太低') } else { target[key] = val return true } } } }) // 主办方 console.log(agent.name) console.log(agent.age) console.log(agent.phone) console.log(agent.price) // 想自己提供报价(砍价,或者高价争抢) agent.customPrice = 150000 // agent.customPrice = 90000 // 报错:价格太低 console.log('customPrice', agent.customPrice)

    二、外观模式

    外观模式,为子系统中的一组接口提供了一个高层的接口,使用者使用这个高层接口。对于外观模式的理解,可以举例。比如在去医院看病,接待员去挂号、门诊、划价和取药。外观模式的设计原则验证,不符合单一职责原则和开放封闭原则,因此谨慎使用,不可滥用。

    三、观察者模式

    观察者模式,发布和订阅,一对多。对于观察者模式的理解,可以举例。比如在咖啡馆点好咖啡以后,就可以玩手机、打游戏等,点好之后坐等被叫。对于观察者模式的应用场景,网页事件绑定,Promise,jQuery callbacks 和 nodejs 自定义事件等等。在 node.js 中,处理 http 请求,多进程通讯。vue 和 react 组件生命周期触发, vue watch 等。观察者模式的设计原则验证,主题和观察者分离,不是主动触发而是被动监听,两者解耦,符合开放封闭原则。

    观察者模式的代码,如下所示:

    // 主题,接收状态变化,触发每个观察者 class Subject { constructor() { this.state = 0 this.observers = [] } getState() { return this.state } setState(state) { this.state = state this.notifyAllObservers() } attach(observer) { this.observers.push(observer) } notifyAllObservers() { this.observers.forEach(observer => { observer.update() }) } } // 观察者,等待被触发 class Observer { constructor(name, subject) { this.name = name this.subject = subject this.subject.attach(this) } update() { console.log(`${this.name} update, state: ${this.subject.getState()}`) } } // 测试代码 let s = new Subject() let o1 = new Observer('o1', s) let o2 = new Observer('o2', s) let o3 = new Observer('o3', s) s.setState(1) s.setState(2) s.setState(3)

    四、迭代器模式

    迭代器模式,顺序访问一个集合,使用者无需知道集合的内部结构和封装。对于迭代器的应用场景,比如 jQuery each、ES6 Iterator 等等。ES6 Iterator 的存在原因是 ES6语法中,有序集合的数据类型已经有很多,需要有一个统一的遍历接口来遍历所有的数据类型。Object 不是有序集合,可以用 Map 代替。ES6 Iterator 具有 [Symbol.iterator] 属性,属性值是函数,执行函数返回一个迭代器。这个迭代器就有 next 方法可顺序迭代子元素,可以运行 Array.prototype[Symbol.iterator] 来测试。ES6 Iterator 与 Generator 的区别是 Iterator 的价值是不限于上述几个类型的遍历,还有 Generator 函数的使用。只要返回的数据符合 Iterator 接口的要求,就可以使用 Iterator 的语法,这就是迭代器模式。迭代器模式的设计原则验证,迭代器对象和目标对象分离,迭代器将使用者与目标对象隔离开,符合开放封闭原则。

    迭代器模式的代码,如下所示:

    class Iterator { constructor(conatiner) { this.list = conatiner.list this.index = 0 } next() { if (this.hasNext()) { return this.list[this.index++] } return null } hasNext() { if (this.index >= this.list.length) { return false } return true } } class Container { constructor(list) { this.list = list } // 生成遍历器 getIterator() { return new Iterator(this) } } // 测试代码 let container = new Container([1, 2, 3, 4, 5]) let iterator = container.getIterator() while(iterator.hasNext()) { console.log(iterator.next()) }
    Processed: 0.012, SQL: 9