内容简介:Talk is cheap. Show me the code. (译: 屁话少说, 放码过来)从代码中很容易看得出来, 我们就是想实现一个简单的计费功能. 可现实中, 商品的价格可能并不是一成不变的.
Talk is cheap. Show me the code. (译: 屁话少说, 放码过来)
以下所有代码参见 Design pattern transformation .
// 商品的信息: 价格 & 折扣 const data = { price: 100, discount: 0.8 } // 顾客信息: 是否威会员 & 购买数量 & 总消费 & 购买时间戳 const customer = { "VIP": true, "quantity": 10, "total": 0, } // 总消费计算方式 total = (info) => { if(!info.VIP) { info.total = data.price * info.quantity; } else { info.total = data.price * data.discount * info.quantity; } } total(customer); console.log('customer', customer); // customer { VIP: true, quantity: 10, total: 800 } 复制代码
从代码中很容易看得出来, 我们就是想实现一个简单的计费功能. 可现实中, 商品的价格可能并不是一成不变的.
data.price = 200
价格变动后, 我们需要及时地获取总消费, 那么就必须重新调用下 total 计费.
total(customer); console.log('customer', customer); // customer { VIP: true, quantity: 10, total: 1600 } 复制代码
这是一个大数据时代, 任何数据都有价值. 现在, 我们还想要每次购买时的时间点.
const customer = { "VIP": true, "quantity": 10, "total": 0, + "timeStamp": 0 } // 获取购买时间 purchaseTime = (info) => { info.timeStamp = Date.now(); } 复制代码
于是, 我们需要执行的函数就多了一个.
total(customer) purchaseTime(customer) console.log('customer', customer) // { VIP: true, quantity: 10, total: 1600, timeStamp: 1542293676297 } 复制代码
如果我们的需求还有很多, 而且不知一个customer呢. 那么, 每次价格变化我们需要执行很多步骤, 每次啊, 麻烦得很.
+ const customer1 = { + "VIP": false, + "quantity": 8, + "total": 0, + "timeStamp": 0 + } total(customer) purchaseTime(customer) func(customer) ... funcN(customer1) total(customer1) purchaseTime(customer1) func(customer1) ... funcN(customer) ... funcN(customerN) 复制代码
现在我们就对上面的代码进行观察者模式改造.
用观察者模式改造
从上面的例子中:chestnut::mahjong:️不难看出, 每次价格变化时, 我们都需要重复调用满足需求的方法. 不妨想想, 如果我们把这些方法存储起来, 等到价格变化时再去统一调用, 岂不是很方便. 那么问题来了, 这和之前所说的观察者模式(从观察者模式说起)有什么区别呢? 在此, 我们试着用观察者模式改造下. 首先观察者模式都是一个套路. 先一个类维护一个列表, 对列表有增删和通知更新功能. 另一个类则是提供了更新接口.
// 观察目标类 class Subject { constructor() { this.observerList = [] } addObserver(observer) { this.observerList.push(observer) } notify(params) { this.observerList.forEach(observer => { observer.update(params) }) } } // 观察者类 class Observer { constructor(fn) { this.update = fn } } 复制代码
接着, 把我们想要调用的方法包装一下, 存储起来.
// 将要重复使用的包装一下 observer1 = new Observer(total) observer2 = new Observer(purchaseTime) // 存起来 let subject = new Subject() subject.addObserver(observer1) subject.addObserver(observer2) 复制代码
每次价格改变时, 只需要通知一下即可.
// 调整商品价格 data.price = 100 subject.notify(customer) subject.notify(customer1) 复制代码
改造结束. 初看起来, 可能变得繁琐了. 但是, 遇到复杂的情况, 这不失是一个好办法. 接下来, 我们看看结合Objec.defineProperty会有什么惊喜.
与Objec.defineProperty结合
支付宝的花呗都可以自己还钱了 , 我们为什么还要别人管着:smirk:. 大家都知道经过Objec.defineProperty处理的对象, 在设置和获取对象属性的时候, 会自动触发响应set和get方法. 利用这一点, 我们就可以做到生活自理了. 熟悉的配方, 熟悉的味道. 熟悉的套路我们不妨再走一遍.
// 观察目标类 class Dependency { constructor() { this.watcherList = [] } addObserver(observer) { this.watcherList.push(observer) } notify(params) { this.watcherList.forEach(watcher => { watcher.update(params) }) } } // 观察类 class Watcher { constructor(fn) { this.update = fn } } 复制代码
我们此行的目的, 是要在data.price 或data.discount改变时, 程序能够自动触发, 得到我们想要的结果. 换句话说, 通知更新的时机是在设置data.price或data.discount的时候.
Object.keys(data).forEach(key => { let value = data[key] const dep = new Dependency() Object.defineProperty(data, key, { set(newVal) { value = newVal dep.notify() }, get() { return value } }) }) 复制代码
对象的每个属性都给了一个依赖实例, 管理自己的依赖. 考虑到customer有很多个, 需要通知到位. 另外, 添加依赖和管理依赖, 前者是因, 后者是果. 在管理之前我们需要想好怎么添加依赖. 回头看一看.
// 总消费计算方式 total = (info) => { if(!info.VIP) { info.total = data.price * info.quantity; } else { info.total = data.price * data.discount * info.quantity; } } // 获取购买时间 purchaseTime = (info) => { info.timeStamp = Date.now(); } 复制代码
我们发现, total 函数依赖于data.price或data.discount的. 如果我们在获取属性时去添加依赖倒是一个好时机.
class Dependency { // 省略 } + Dependency.targey = null; class Watcher { constructor(fn, key) { this.update = fn + this.key = key + this.value = this.getter() } + getter() { + Dependency.targey = this; + // 出发下面的get() + this.value = data[this.key]; + Dependency.targey = null; + } } Object.keys(data).forEach(key => { let value = data[key] const dep = new Dependency() Object.defineProperty(data, key, { set(newVal) { value = newVal dep.notify() }, get() { + if (Dependency.targey) { + dep.addObserver(Dependency.targey) + } return value } }) }) 复制代码
然而 purchaseTime 方法里并没有data.price或data.discount可以设置. 所以这个方法行不通. 那么, 干脆紧接着依赖实例去添加依赖吧. 同时考虑到多个customer, 我们封装下.
// 与defineProperty结合 function defineReactive(data, watcherList, funcList) { Object.keys(data).forEach(key => { let value = data[key] const dep = new Dependency() funcList.forEach(func => { dep.addObserver(new Watcher(func)) }) Object.defineProperty(data, key, { set(newVal) { value = newVal watcherList.forEach(watcher => { dep.notify(watcher) }) }, get() { return value } }) }) } defineReactive(data, [customer, customer1], [total, purchaseTime]) 复制代码
大功告成, 价格变动时, 我们就会自动获取到想要的结果了. 我都能自理了, 你花呗为嘛还不能自己还钱呢:unamused:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。