3-7 观察者模式的理解
本节编程练习不计算学习进度,请电脑登录imooc.com操作

观察者模式的理解

圣诞主题也少量运用一些设计模式,结合下实际的代码,让大家对模式的实际运用有一定的理解。这里主要讲解下最常见"观察者模式"的概念,并且在后面的代码会实际运用到

理论上说,观察者模式又叫发布-订阅模式,就是定义一个一对多的依赖关系,当一个对象的状态发生改变,所依赖它的对象将会得到这个改变的通知。在JS的实现级上,一般通过事件的方式模拟发布-订阅模式

简单的来说,我们可以这样想下,去商场买衣服,刚好断货了,特别喜欢的话就会留下电话,等待卖家通知,所以就是一个典型的发布-订阅的关系,买家不需要每天去询问是否到货,卖家到后才会主动通知

说了半天,在JS中用这个东东具体干嘛呢? 设计模式的核心就是为了解耦,解耦,解耦重要事说三遍。通过这个模式,一个对象可以不再显示的调用另外一个对象的接口了,或者不关心对象的内部运行状态,只对感兴趣的事件进行观察了,具体用法后续的页面切换会讲解到,这里先理解下如何实现这个模式的简单逻辑。

JS里对观察者模式的实现是通过回调来实现的,参考右边的代码observer.js,其内部包含了3个方法:订阅、退订、发布也就是对应的bind、unbind、trigger方法

创建一个观察者对象

var observer = new Observer();

通过调用subscribe方法,实现一个事件的观察

observer.subscribe("任务名",处理函数)

通过publish触发观察事件,在任意时刻触发了这个观察的任务名,将会触发这个事件订阅subscribe方法

 observer.publish("任务名")

取消事件订阅,意味着就不会执行了

 observer.unsubscribe("任务名")

观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化。概念是有一点抽象的,等到后面实际结合理解。

任务

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  6. <title>圣诞主题</title>
  7. <script src="observer.js"></script>
  8. </head>
  9.  
  10. <body>
  11. <button>点击触发page任务</button>
  12. <button>点击取消page任务</button>
  13. <script type="text/javascript">
  14. //观察者
  15. var observer = new Observer();
  16.  
  17. //page函数
  18. //模拟运行1秒的一个函数
  19. function page() {
  20. setTimeout(function() {
  21. //发布一个完成的时间
  22. observer.publish("compelte")
  23. }, 1000)
  24. }
  25.  
  26. //订阅一个完成的时间
  27. observer.subscribe("compelte", function() {
  28. alert("page任务完成")
  29. })
  30.  
  31. //触发任务
  32. document.querySelectorAll("button")[0].addEventListener("click", function() {
  33. page();
  34. }, false)
  35.  
  36. //取消任务
  37. document.querySelectorAll("button")[1].addEventListener("click", function() {
  38. observer.unsubscribe("compelte")
  39. alert("注销了订阅了compelte事件")
  40. }, false)
  41. </script>
  42. </body>
  43.  
  44. </html>
  45.  
  1. /**
  2.  * 事件
  3.  * 观察者模式
  4.  */
  5. var Observer = (function(slice) {
  6.  
  7. function bind(event, fn) {
  8. var events = this.events = this.events || {},
  9. parts = event.split(/\s+/),
  10. i = 0,
  11. num = parts.length,
  12. part;
  13.  
  14. if (events[event] && events[event].length) return this;
  15.  
  16. for (; i < num; i++) {
  17. events[(part = parts[i])] = events[part] || [];
  18. events[part].push(fn);
  19. }
  20. return this;
  21. }
  22.  
  23. function one(event, fn) {
  24. this.bind(event, function fnc() {
  25. fn.apply(this, slice.call(arguments));
  26. this.unbind(event, fnc);
  27. });
  28. return this;
  29. }
  30.  
  31. function unbind(event, fn) {
  32. var events = this.events,
  33. eventName, i, parts, num;
  34.  
  35. if (!events) return;
  36.  
  37. parts = event.split(/\s+/);
  38. for (i = 0, num = parts.length; i < num; i++) {
  39. if ((eventName = parts[i]) in events !== false) {
  40. events[eventName].splice(events[eventName].indexOf(fn), 1);
  41. if (!events[eventName].length) { //修正没有事件直接删除空数组
  42. delete events[eventName];
  43. }
  44. }
  45. }
  46. return this;
  47. }
  48.  
  49. function trigger(event) {
  50. var events = this.events,
  51. i, args, falg;
  52.  
  53. if (!events || event in events === false) return;
  54.  
  55. args = slice.call(arguments, 1);
  56. for (i = events[event].length - 1; i >= 0; i--) {
  57. falg = events[event][i].apply(this, args);
  58. }
  59. return falg; //修正带返回
  60. }
  61.  
  62. return function() {
  63. this.on =
  64. this.subscribe = bind;
  65. this.off =
  66. this.unsubscribe = unbind;
  67. this.trigger =
  68. this.publish = trigger;
  69. this.one = one;
  70. return this;
  71. };
  72.  
  73. })([].slice);
  74.  
下一节