用户与页面的交互会导致数据状态发生变化,数据状态变化,又需要通过 UI 表现出来。随着页面复杂度的提高,数据变化后要通知的 UI 组件也会变得越来越多。如果不对这一步进行解耦,这部分的代码会变得越来越冗余和复杂,对代码的可读性和可测试性都带来不良的影响。
所以,我们需要对【数据变化】 -> 【UI 变化】这部分的逻辑进行解耦
观察者模式介绍 观察者模式(Observer pattern)是一种管理对象及其行为和状态之间的关系的得力工具。
用 JavaScript 的话来说,这种模式的实质就是对可以对程序中的某个对象的状态进行观察,并且在其发生改变时能够得到通知。
所以,这个模式可以解决我们现在面对的问题:”对【数据变化】 -> 【UI 变化】这部分的逻辑进行解耦”
观察者模式中存在两个角色:观察者和被观察者(又名订阅者和发布者)。下面是观察者模式的实现原理。
观察者模式实现原理 可以通过一个实例来了解观察者模式的运行原理。
首先,建立一个描述观察者的类。
1 2 3 4 5 function Observer ( ) { this .Update = function ( ) { }; }
然后,建立一个描述观察者队列的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 function ObserverList ( ) { this .observerList = []; } ObserverList.prototype.Add = function (obj ) { return this .observerList.push(obj); }; ObserverList.prototype.Empty = function ( ) { this .observerList = []; }; ObserverList.prototype.Count = function ( ) { return this .observerList.length; }; ObserverList.prototype.Get = function (index ) { if (index > -1 && index < this .observerList.length) { return this .observerList[index]; } }; ObserverList.prototype.Insert = function (obj, index ) { var pointer = -1 ; if (index === 0 ) { this .observerList.unshift(obj); pointer = index; } else if (index === this .observerList.length) { this .observerList.push(obj); pointer = index; } return pointer; }; ObserverList.prototype.IndexOf = function (obj, startIndex ) { var i = startIndex, pointer = -1 ; while (i < this .observerList.length) { if (this .observerList[i] === obj) { pointer = i; } i++; } return pointer; }; ObserverList.prototype.RemoveAt = function (index ) { if (index === 0 ) { this .observerList.shift(); } else if (index === this .observerList.length - 1 ) { this .observerList.pop(); } }; function extend (extension, obj ) { for (var key in extension) { obj[key] = extension[key]; } }
接着,建立一个描述被观察者的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function Subject ( ) { this .observers = new ObserverList(); } Subject.prototype.AddObserver = function (observer ) { this .observers.Add(observer); }; Subject.prototype.RemoveObserver = function (observer ) { this .observers.RemoveAt(this .observers.IndexOf(observer, 0 )); }; Subject.prototype.Notify = function (context ) { var observerCount = this .observers.Count(); for (var i = 0 ; i < observerCount; i++) { this .observers.Get(i).Update(context); } };
注意这里 Subject 类的 Notify 方法。在观察者模式中,观察者可以观察到被观察者,原因就是:被观察者把观察者的引用存储起来,被观察者可以进行”通告”,对所有观察者进行调用(发送信息)。
下面是一个具体的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <html> <head></head> <body> <button id="addNewObserver">Add New Observer checkbox</ button> <input id="mainCheckbox" type="checkbox" /> <div id="observersContainer" ></div> <script src="./ observer-pattern.js"></script> <script> // 我们DOM 元素的引用 var controlCheckbox = document.getElementById(" mainCheckbox"), addBtn = document.getElementById(" addNewObserver"), container = document.getElementById(" observersContainer"); // 具体的被观察者 //Subject 类扩展controlCheckbox 类 extend(new Subject(), controlCheckbox); //点击checkbox 将会触发对观察者的通知 controlCheckbox[" onclick"] = new Function(" controlCheckbox.Notify(controlCheckbox.checked)"); addBtn[" onclick"] = AddNewObserver; // 具体的观察者 function AddNewObserver() { //建立一个新的用于增加的checkbox var check = document.createElement(" input"); check.type = " checkbox"; // 使用Observer 类扩展checkbox extend(new Observer(), check); // 使用定制的Update函数重载 check.Update = function (value) { this.checked = value; }; // 增加新的观察者到我们主要的被观察者的观察者列表中 controlCheckbox.AddObserver(check); // 将元素添加到容器的最后 container.appendChild(check); } </script> </body> </html>
至此,观察者模式使用方式简述完毕。
参考 《JavaScript 设计模式与开发实践》
Learning JavaScript Design Patterns Learning JavaScript Design Patterns 中文版