原文链接:进击的视察者形式
商品信息转变带来的懊恼
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)
如今我们就对上面的代码举行视察者形式革新.
用视察者形式革新
从上面的例子中🌰🀄️不难看出, 每次价钱变化时, 我们都须要反复挪用满足需求的要领. 无妨想一想, 假如我们把这些要领存储起来, 比及价钱变化时再去一致挪用, 岂不是很轻易. 那末题目来了, 这和之前所说的视察者形式(从视察者形式提及)有什么区别呢? 在此, 我们试着用视察者形式革新下.
起首视察者形式都是一个套路. 先一个类保护一个列表, 对列表有增删和关照更新功用. 另一个类则是供应了更新接口.
// 视察目标类
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连系
支付宝的花呗都能够本身还钱了🤣, 我们为何还要他人管着😏. 人人都晓得经由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])
功德圆满, 价钱更改时, 我们就会自动猎取到想要的效果了. 我都能自理了, 你花呗为嘛还不能本身还钱呢😒