观察者模式(Observer),又叫做发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。主题对象状态发生改变时,会通知所有的观察者对象,使它们能自动更新自己。当不需要监听某一个特定的主题对象时,可以取消订阅,即从观察者列表中删除。为此可以确定如下的类。
ES5
//继承的核心方法
Object.extend = function(obj, extension) {
for (const key in extension) {
obj[key] = extension[key];
}
return obj;
};
Object.prototype.extend = function(object) {
return Object.extend.apply(this, [this, object]);
};
//通知的接口
function Subject() {
}
Subject.prototype.attach = function(observer) {
};
Subject.prototype.detach = function(observer) {
};
Subject.prototype.notify = function() {
}
//抽像观察者类
function Observer() {
this.update = function() {
}
};
function ConcreteObserver() {
this.content = "";
}
ConcreteObserver.prototype = (new Observer().extend({
update: function(msg) {
this.content = msg
}
}));
//具体的通知者类
function ConcreteSubject() {
this.observers = [];
}
//继承后实现具体的方法,并保持链式调用
ConcreteSubject.prototype = (new Subject()).extend({
attach: function(observer) {
this.observers.push(observer);
return this;
},
detach: function(observer) {
this.observers.splice(this.observers.indexOf(observer), 1);
return this;
},
notify: function(msg) {
for (let i = 0, len = this.observers.length; i < len; i++) {
this.observers[i].update(msg);
}
return this;
}
});
const subject = new ConcreteSubject();
const observer1 = new ConcreteObserver();
const observer2 = new ConcreteObserver();
//追加观察对象
subject.attach(observer1).attach(observer2).notify("初始化状态");
console.log(subject);
//取消某一对象的观察
subject.detach(observer1).notify('更改状态');
console.log(subject);
ES6
class Subject{
constructor(){
}
attach(){
}
detach(){
}
notify(){
}
}
class Observer{
constructor(){
}
update(){
}
}
class ConcreteObserver extends Observer {
constructor(){
super();
this.content="";
}
update(msg){
this.content=msg;
}
}
class ConcreteSubject extends Subject{
constructor(){
super();
this.observers=[];
}
attach(observer){
this.observers.push(observer);
return this;
}
detach(observer){
this.observers.splice(this.observers.indexOf(observer), 1);
return this;
}
notify(msg){
for (let i = 0, len = this.observers.length; i < len; i++) {
this.observers[i].update(msg);
}
return this;
}
}
const subject = new ConcreteSubject();
const observer1=new ConcreteObserver();
const observer2=new ConcreteObserver();
//追加观察对象
subject.attach(observer1).attach(observer2).notify("初始化状态");
console.log(subject);
//取消某一对象的观察
subject.detach(observer1).notify('更改状态');
console.log(subject);
复选框选中:
(1)实现复选框选中一般情况下可采取这样的方法:document.getElementByName('checkbox')
获取到checkbox
节点集合,然后for循环
遍历设置其checked
属性就可以了。但是如果复选框选中需要实现多个复杂业务如全选,首尾选中,奇数个选中,偶数个选中等功能,就需要在每个功能中实现如获取节点集合->for循环遍历->if条件判断等流程,而且获取节点结合等DOM查询在每个功能中都耦合了,DOM结构改变了所有涉及到获取节点集合的代码都需要修改。
(2)对于使用观察者模式,即可以在全局重缓存主题类,所有需要改变属性的DOM节点都可以加入到观察者集合里,包括未来新增的DOM节点也可以加入到观察者集合里。使得需要改变属性的DOM节点得到缓存,减少了DOM查询,业务代码集中放到Subject
类去实现,使得代码更清晰,易维护。
<html>
<title>观察者设计模式title>
<head>
<meta charset="utf-8">
<style type="text/css">
style>
head>
<body>
<div><label>普通新增选项:<button id="normalAddCheckBox">添加button>div>
<div><label>普通全选:<input type="checkbox" id="normalAllCheck"/>label>div>
<div><label>其他普通选中:<button id="normalOddCheck">奇数个选中button>label>div>
<hr/>
<div><label>观察者新增选项:<button id="observerAddCheckBox">添加button>div>
<div><label>观察者全选:<input type="checkbox" id="observerAllCheck"/>div>
<div><label>其他观察者选中:<button id="observerOddCheck">奇数个选中button><button id="firstLastCheck">首尾选中button>label>div>
<hr/>
<div>
普通新增选项区域:
<div id="normalContainer">div>
div>
<hr/>
<div>
观察者新增选项区域
<div id="observerContainer">div>
div>
<script type="text/javascript">
document.getElementById('normalAddCheckBox').onclick = function() {
const checkbox = document.createElement('input');
checkbox.type = "checkbox";
checkbox.name = "checkbox";
document.getElementById('normalContainer').appendChild(checkbox);
}
//全选
document.getElementById('normalAllCheck').onchange = function(event) {
const checkbox = document.getElementsByName('checkbox');
for (const item of checkbox) {
item.checked = event.target.checked;
}
}
//奇数个选中
document.getElementById('normalOddCheck').onclick=function(){
const checkbox = document.getElementsByName('checkbox');
for(let i=0,len=checkbox.length;i<len;i++){
if(i%2!=0){
checkbox[i].checked=true;
}
}
}
script>
<script type="text/javascript">
class CheckboxSubject {
constructor() {
this.observers = [];
return this;
}
attach(checkbox) {
this.observers.push(checkbox);
return this;
}
detach(checkbox) {
this.observers.splice(this.observers.indexOf(observer), 1);
return this;
}
//全选
notify(checked) {
for (let i = 0, len = this.observers.length; i < len; i++) {
this.observers[i].update(checked);
}
}
//奇数选中
oddCheck(checked) {
for (let i = 0, len = this.observers.length; i < len; i++) {
if (i % 2 !== 0) {
this.observers[i].update(checked);
}
}
}
//首尾选中
firstLastCheck(checked){
if(this.observers.length!==0){
this.observers[0].update(checked);
this.observers[this.observers.length-1].update(checked);
}
}
//更多选中方式可自定义
}
const checkboxSubject = new CheckboxSubject();
document.getElementById('observerAddCheckBox').onclick=function(){
const checkbox = document.createElement('input');
checkbox.type = "checkbox";
checkbox.name = "checkbox";
checkbox.update=function(value){
this.checked=value;
}
document.getElementById('observerContainer').appendChild(checkbox);
checkboxSubject.attach(checkbox);
}
//全选
document.getElementById('observerAllCheck').onchange = function(event) {
checkboxSubject.notify(event.target.checked);
};
//奇数个选中
document.getElementById('observerOddCheck').onclick=function(){
checkboxSubject.oddCheck(true);
}
document.getElementById('firstLastCheck').onclick=function(){
checkboxSubject.firstLastCheck(true);
}
script>
body>
html>
实现效果
从上图可以看到,观察者模式只对在观察者集合里的元素进行状态的改变。
观察者模式在前端开发中可应用于数据绑定,事件委托,事件集合等如Vue
中的this.$emit();this.$on()
,Jquery
里的$('#demo').on()
等都用到了观察者模式(发布/订阅模式)的思想。个人觉得,对于前端开发,设计到批量操作的业务,接耦不同的功能代码如(业务订阅某一主题,主题内根据传入的msg实现具体的业务,Ajax后发布某一主题并传如msg)
等也可以吸收观察者模式的思想,应用于开发过程中。