共计 5328 个字符,预计需要花费 14 分钟才能阅读完成。
最近把之前学习过的这些设计模式又再次复习了一下,感觉还是有很多播种的。的确有了 温故知新 的感觉,所以筹备在每个设计模式温习完之后都可能写一篇对于这个设计模式的文章,这样会让本人可能加深对这个设计模式的了解;也可能跟大家一起来探讨一下。
明天咱们来一起学习一下 观察者模式 ,刚开始咱们不须要晓得观察者模式的定义是什么,这些咱们到前面再去理解。我想先带着大家从生存中的一个小事例开始。 从生存中相熟的事件动手,会让咱们更疾速的了解这个模式的用处。
生存中的小例子
置信大家都关注过一些公众号,那么对于一个公众号来说,如果有新的文章公布的话;那么所有关注这个公众号的用户都会收到更新的告诉,如果一个用户没有关注或者关注后又勾销了关注,那么这个用户就不会收到该公众号更新的告诉。置信这个场景大家都很相熟吧。那么如果咱们把这个过程形象进去,用代码来实现的话,你会怎么解决呢?无妨当初停下来思考一下。
通过下面的形容,咱们晓得这是一个 一对多的关系。也就是一个公众号对应着许多关注这个公众号的用户。
那么对于这个公众号来说,它的外部须要有一个列表记录着关注这个公众号的用户 ,一旦公众号有了新的内容。那么对于公众号来说, 它会遍历这个列表。而后给列表中的每一个用户发送一个内容跟新的告诉。咱们能够通过代码来示意这个过程:
// 用户
const user = {update() {console.log('公众号更新了新的内容');
},
};
// 公众号
const officialAccount = {
// 关注以后公众号的用户列表
followList: [user],
// 公众号更新时候调用的告诉函数
notify() {
const len = this.followList.length;
if (len > 0) {
// 告诉已关注该公众号的每个用户,有内容更新
for (let user of this.followList) {user.update();
}
}
},
};
// 公众号有新内容更新
officialAccount.notify();
运行的后果如下:
公众号更新了新的内容
下面的代码可能简略的示意,当公众号的内容产生了更新的时候,去告诉关注该公众号的用户的过程。然而这个实现是很简陋的,还短少一些内容。咱们接下来把这些短少的过程补充残缺。对于公众号来说,还须要能够增加新的关注的用户,移除不再关注的用户,获取关注公众号的用户总数等。咱们来实现一下下面的过程:
// 公众号
const officialAccount = {
// 关注以后公众号的用户列表
followList: [],
// 公众号更新时候调用的告诉函数
notify() {
const len = this.followList.length;
if (len > 0) {
// 告诉已关注该公众号的每个用户,有内容更新
for (let user of this.followList) {user.update();
}
}
},
// 增加新的关注的用户
add(user) {this.followList.push(user);
},
// 移除不再关注的用户
remove(user) {const idx = this.followList.indexOf(user);
if (idx !== -1) {this.followList.splice(idx, 1);
}
},
// 计算关注公众号的总的用户数
count() {return this.followList.length;},
};
// 新建用户的类
class User {constructor(name) {this.name = name;}
// 接管公众号内容更新的告诉
update() {console.log(`${this.name}接管到了公众号的内容更新 `);
}
}
// 创立两个新的用户
const zhangSan = new User('张三');
const liSi = new User('李四');
// 公众号增加关注的用户
officialAccount.add(zhangSan);
officialAccount.add(liSi);
// 公众号有新内容更新
officialAccount.notify();
console.log(` 以后关注公众号的用户数量是:${officialAccount.count()}`);
// 张三不再关注公众号
officialAccount.remove(zhangSan);
// 公众号有新内容更新
officialAccount.notify();
console.log(` 以后关注公众号的用户数量是:${officialAccount.count()}`);
输入的后果如下:
张三接管到了公众号的内容更新
李四接管到了公众号的内容更新
以后关注公众号的用户数量是:2
李四接管到了公众号的内容更新
以后关注公众号的用户数量是:1
下面的代码欠缺了关注和勾销关注的过程,并且能够获取以后公众号的关注人数。咱们还实现了一个用户类,可能让咱们疾速创立须要增加到公众号关注列表的用户。当然你也能够把公众号的实现通过一个类来实现,这里就不再展现实现的过程了。
通过下面这个简略的例子,你是不是有所感悟,有了一些新的播种?咱们下面实现的其实就是一个简略的观察者模式。接下来咱们来聊一聊观察者模式的定义,以及一些在理论开发中的用处。
观察者模式的定义
所谓的观察者模式指的是一种一对多的关系,咱们把其中的 一
叫做 Subject
(类比上文中的公众号),把其中的 多
叫做 Observer
(类比上文中关注公众号的用户),也就是观察者。因为多个Observer
的变动依赖 Subject
的状态更新,所以 Subject
在外部保护了一个 Observer
的列表,一旦 Subject
的状态有更新,就会遍历这个列表,告诉列表中每一个 Observer
进行相应的更新。因为有了这个列表,Subject
就能够对这个列表进行增删改查的操作。也就实现了 Observer
对Subject
依赖的更新和解绑。
咱们来看一下观察者模式的 UML
图:
从上图咱们这能够看到,对于 Subject
来说,它本身须要保护一个 observerCollection
,这个列表外面就是Observer
的实例。而后在 Subject
外部实现了减少观察者,移除观察者,和告诉观察者的办法。其中告诉观察者的形式就是遍历 observerCollection
列表,顺次调用列表中每一个 observer
的update
办法。
到这里为止,你当初曾经对这个设计模式有了一些理解。那咱们学习这个设计模式有什么作用呢?首先如果咱们在开发中遇到这种相似下面的一对多的关系,并且 多
的状态更新依赖 一
的状态;那么咱们就能够应用这种设计模式去解决这种问题。而且咱们也能够应用这种模式解耦咱们的代码,让咱们的代码更好拓展与保护。
当然一些同学会感觉本人在平时的开发中如同没怎么应用过这种设计模式,那是因为咱们平时在开发中个别都会应用一些框架,比方 Vue
或者 React
等,这个设计模式曾经被这些框架在外部实现好了。咱们能够间接应用,所以咱们对这个设计模式的感知会少一些。
实战:实现一个简略的 TODO 小利用
咱们能够应用观察者模式实现一个小利用,这个利用很简略,就是可能让用户增加本人的待办,并且须要显示已增加的待办事项的数量。
理解了需要之后,咱们须要确定那些是 一
,哪些是 多
。当然咱们晓得整个 TODO 的 状态
就是咱们所说的 一
,那么对于 待办列表的展现
以及 待办列表的计数
就是咱们所说的 多
。理清了思路之后,实现这个小利用就变得很简略了。
能够点击???? 这里提前体验一下这个简略的小利用。
首先咱们须要先实现观察者模式中的 Subject
和Observer
类,代码如下所示。
Subject
类:
// Subject
class Subject {constructor() {this.observerCollection = [];
}
// 增加观察者
registerObserver(observer) {this.observerCollection.push(observer);
}
// 移除观察者
unregisterObserver(observer) {const observerIndex = this.observerCollection.indexOf(observer);
this.observerCollection.splice(observerIndex, 1);
}
// 告诉观察者
notifyObservers(subject) {
const collection = this.observerCollection;
const len = collection.length;
if (len > 0) {for (let observer of collection) {observer.update(subject);
}
}
}
}
Observer
类:
// 观察者
class Observer {update() {}}
那么接下来的代码就是对于下面待办的具体实现了,代码中也增加了相应的正文,咱们来看一下。
待办利用的逻辑局部:
// 表单的状态
class Todo extends Subject {constructor() {super();
this.items = [];}
// 增加 todo
addItem(item) {this.items.push(item);
super.notifyObservers(this);
}
}
// 列表渲染
class ListRender extends Observer {constructor(el) {super();
this.el = document.getElementById(el);
}
// 更新列表
update(todo) {super.update();
const items = todo.items;
this.el.innerHTML = items.map(text => `<li>${text}</li>`).join('');
}
}
// 列表计数观察者
class CountObserver extends Observer {constructor(el) {super();
this.el = document.getElementById(el);
}
// 更新计数
update(todo) {this.el.innerText = `${todo.items.length}`;
}
}
// 列表观察者
const listObserver = new ListRender('item-list');
// 计数观察者
const countObserver = new CountObserver('item-count');
const todo = new Todo();
// 增加列表观察者
todo.registerObserver(listObserver);
// 增加计数观察者
todo.registerObserver(countObserver);
// 获取 todo 按钮
const addBtn = document.getElementById('add-btn');
// 获取输入框的内容
const inputEle = document.getElementById('new-item');
addBtn.onclick = () => {
const item = inputEle.value;
// 判断增加的内容是否为空
if (item) {todo.addItem(item);
inputEle.value = '';
}
};
从下面的代码咱们能够分明地晓得这个利用的每一个局部,被察看的 Subject
就是咱们的 todo
对象,它的状态就是待办列表。它保护的观察者列表别离是展现待办列表的 listObserver
和展现待办数量的 countObserver
。一旦todo
的列表新减少了一项待办,那么就会告诉这两个观察者去做相应的内容更新。这样代码的逻辑就很直观明了。如果当前在状态变更的时候还要增加新的性能,咱们只须要再次增加一个相应的 observer
就能够了,保护起来也很不便。
当然下面的代码只实现了很根底的性能,还没有蕴含待办的实现和删除,以及对于未实现和已实现的待办的分类展现。而且列表的渲染每次都是从新渲染的,没有复用的逻辑。因为咱们本章的内容是跟大家一起来探讨一下观察者模式,所以下面的代码比拟简陋,也只是为了阐明观察者模式的用法。置信优良的你可能在这个根底上,把这些性能都欠缺好,快去试试吧。
其实咱们学习这些设计模式,都是为了让代码的逻辑更加清晰明了,可能复用一些代码的逻辑,缩小反复的工作,晋升开发的效率。让整个利用更加容易保护和拓展。当然不能为了应用而应用,在应用之前,须要对以后的问题做一个全面的理解。到底需不需要应用某个设计模式是一个须要思考分明的问题。
好啦,对于观察者模式到这里就完结啦,大家如果有什么意见和倡议能够在文章上面上面留言,咱们一起探讨一下。也能够在这里提出来,咱们更好地进行探讨。也欢送大家关注我的公众号 关山不难越,随时随地获取文章的更新。
参考链接:
- The Observer Pattern
- How to Use the Observable Pattern in JavaScript
文章封面图起源:unDraw