자바스크립트 옵져버 패턴(Observer Pattern) 구현



Observer Pattern



흔히 발행/구독 패턴이라 불리는 이 패턴은 객체의 상태 변화를 관찰하는 관찰자(옵져버)를 객체에 등록(구독)하여, 객체에 상태 변화가 있을때마다 등록된 목록의 관찰자들에게 통지(발행) 하도록 하는 디자인 패턴이다.


쉽게 말해 Broadcast System을 구축할 수 있으며, 각 모듈간의 밀접한 커플링을 조율하여, 모듈의 재사용성을 높인다.



아래 코드는 모든 유저(user1, user2, user3)가 현재 까지 등록(구독)된 유저 현황에 대한 정보를 통지(발행)를 받을 수 있다.


  1. // 옵져버 패턴
  2. var Observer = new (function () {
  3.  
  4.     var Observer = function () {
  5.         this.observers = [];
  6.         return this;
  7.     }
  8.  
  9.     Observer.fn = Observer.prototype = {
  10.  
  11.         subscribe: function (type, fn) {
  12.  
  13.             type = type || '';
  14.  
  15.             this.observers.push({ id: this.observers.length - 1, type: type, fn: fn, isStop: false });
  16.  
  17.             return (this.observers.length - 1);
  18.         },
  19.         remove: function (type, id) {
  20.  
  21.             type = type || '';
  22.  
  23.             if (typeof id === 'number') {
  24.                 this.observers[id].type === type && delete this.observers[id];
  25.             }
  26.             else {
  27.                 for (var t in this.observers) {
  28.                     if (type) {
  29.                         if (this.observers[t].type === type) {
  30.                             delete this.observers[t];
  31.                         }
  32.                     }
  33.                     else {
  34.                         delete this.observers[t];
  35.                     }
  36.                 }
  37.             }
  38.  
  39.             return this;
  40.         },
  41.         abort: function (type, id) {
  42.  
  43.             type = type || '';
  44.  
  45.             if (typeof id === 'number') {
  46.                 if (this.observers[id].type === type) this.observers[id].isStop = true;
  47.             }
  48.             else {
  49.                 for (var t in this.observers) {
  50.                     if (type) {
  51.                         if (this.observers[t].type === type) {
  52.                             this.observers[t].isStop = true;
  53.                         }
  54.                     }
  55.                     else {
  56.                         this.observers[t].isStop = true;
  57.                     }
  58.                 }
  59.             }
  60.  
  61.             return this;
  62.         },
  63.         restore: function (type, id) {
  64.  
  65.             type = type || '';
  66.  
  67.             if (typeof id === 'number') {
  68.                 if (this.observers[id].type === type) this.observers[id].isStop = false;
  69.             }
  70.             else {
  71.                 for (var t in this.observers) {
  72.                     if (type) {
  73.                         if (this.observers[t].type === type) {
  74.                             this.observers[t].isStop = false;
  75.                         }
  76.                     }
  77.                     else {
  78.                         this.observers[t].isStop = false;
  79.                     }
  80.                 }
  81.             }
  82.  
  83.             return this;
  84.         },
  85.         publish: function (type, id) {
  86.  
  87.             type = type || '';
  88.  
  89.             if (typeof id === 'number') {
  90.                 typeof this.observers[id].fn === 'function' && this.observers[id].fn.apply(this.observers[id].type, Array.prototype.slice.call(arguments));
  91.             }
  92.             else {
  93.                 for (var t in this.observers) {
  94.                     if (type) {
  95.                         if (this.observers[t].type === type && !this.observers[t].isStop) {
  96.                             typeof this.observers[t].fn === 'function' && this.observers[t].fn.apply(this.observers[t].type, Array.prototype.slice.call(arguments));
  97.                         }
  98.                     }
  99.                     else {
  100.                         if (!this.observers[t].isStop) {
  101.                             typeof this.observers[t].fn === 'function' && this.observers[t].fn.apply(this.observers[t].type, Array.prototype.slice.call(arguments));
  102.                         }
  103.                     }
  104.                 }
  105.             }
  106.  
  107.             return this;
  108.         }
  109.     }
  110.  
  111.     function marge() {
  112.  
  113.         var parent = this
  114.     , target = {};
  115.  
  116.         for (var i = 0, length = arguments.length; i < length; i++) {
  117.  
  118.             target = arguments[i];
  119.  
  120.             for (var n in parent) {
  121.                 if (typeof target[n] === 'undefined') target[n] = parent[n];
  122.             }
  123.         }
  124.  
  125.         return target;
  126.     }
  127.  
  128.     return Observer;
  129.  
  130. } ())();
  131.  
  132. function Users(name, callback) {
  133.     this.name = name;
  134.  
  135.     Observer.subscribe(this, callback);
  136.  
  137.     return this;
  138. }
  139.  
  140. Users.prototype.broadCast = function () {
  141.     alert(this.name + '님 안녕하세요.');
  142. }
  143.  
  144. var user1 = new Users('전성균1', Users.prototype.broadCast);
  145. var user2 = new Users('전성균2', Users.prototype.broadCast);
  146. var user3 = new Users('전성균3', Users.prototype.broadCast);
  147. var uid = Observer.subscribe(user3, Users.prototype.broadCast);
  148.  
  149. // 구독 삭제
  150. //Observer.remove();
  151. //Observer.remove(user1);
  152. //Observer.remove(user3, uid);
  153.  
  154. // 구독 정지
  155. //Observer.abort();
  156. //Observer.abort(user3);
  157. //Observer.abort(user3, uid);
  158.  
  159. // 구독 복구
  160. //Observer.restore();
  161. //Observer.restore(user3);
  162. //Observer.restore(user3, uid);
  163.        
  164. // 통지
  165. //Observer.publish();
  166. //Observer.publish(user3);
  167. //Observer.publish(user3, uid);