Pull to refresh

Используем паттерн Наблюдатель(Observer) для создания индикатора выполнения процесса на Javascript

Reading time4 min
Views49K
Идея паттерна Observer заключается в создании зависимости типа один ко многим. При изменении состояния одного объекта(субъекта), зависящие от него объекты(наблюдатели) об этом оповещаются и обновляются. Это нужно для согласования состояния взаимосвязанных объектов без их жесткой связанности.



Например, при изменении данных A, B, C диаграмма и таблица, отвечающие за представление, должны измениться. При этом неизвестно, сколько имеется представлений.


Паттерн наблюдатель описывает, как реализовать такое отношение. В основе лежат объекты Subject и Observer. Субъект изменяется и уведомляет о своих изменениях зависимым от него Наблюдателям. Наблюдатели синхронизируют свои данных с Субъектом. Также это отношение называют издатель-подписчик. Субъект(издатель) рассылает уведомления своим наблюдателям(подписчикам), даже не зная о том, какие объекты ими являются. При этом количество подписчиков не ограничено.

Как это все будет выглядеть в Javascript?


Объявим издателей
var Microsoft = new Publisher;
var Google = new Publisher;
var Apple = new Publisher;


* This source code was highlighted with Source Code Highlighter.


Теперь подписчиков

var Ann = function(from) {
 console.log('Delivery from '+from+' to Ann');
};
var Vasya = function(from) {
 console.log('Delivery from '+from+' to Vasya');
};
var Maria = function(from) {
 console.log('Delivery from '+from+' to Maria ');
};

* This source code was highlighted with Source Code Highlighter.


Подпишем всех

Ann.subscribe(Microsoft).subscribe(Google).subscribe(Apple);
Vasya.subscribe(Google).subscribe(Apple);
Maria.subscribe(Microsoft);


* This source code was highlighted with Source Code Highlighter.


Отправим подписчикам новости

Microsoft.deliver('news 1').deliver('news 2');
Google.deliver('googlenews 1').deliver('googlenews 2');

* This source code was highlighted with Source Code Highlighter.


Итак, теперь напишем конструктор для издателя. Внутри него хранятся подписчики.

function Publisher() {
  this.subscribers = [];
}


* This source code was highlighted with Source Code Highlighter.


Метод издателя, отправляющего своим подписчикам уведомление(расширение forEach для массивов можно взять с developer.mozilla.org)

Publisher.prototype.deliver = function(data) {

  this.subscribers.forEach(

    function(fn) {

      fn(data);

    }

  );

  return this;

};


* This source code was highlighted with Source Code Highlighter.


И добавим в Function метод subscribe для добавления подписчика.
Function.prototype.subscribe = function(publisher) {
 publisher.subscribers.push(this);
 return this;
};


* This source code was highlighted with Source Code Highlighter.


Ну вот. Паттерн готов.

Как это все использовать?


Я покажу как использовать данный паттерн для индикатора процесса длительного действия. Этим действием может быть обработка большого массива данных, загрузка файла на сервер, длительное передвижение какого-либо персонажа и, может когда-нибудь возможно будет во всех браузерах, обработка XHR до наступления readyState=4.

У меня будет передвигается панда по тропинке Сразу пример.
var Animal=function(id){
  this.onGo=new Publisher();
  this.id=id;
  this.domElement=$('#'+id);
  this.from=parseInt(this.domElement.css('left'));
}
Animal.prototype.go=function(to,duration){
  var start = new Date().getTime();
  var that=this;
  setTimeout(function() {
    var now = (new Date().getTime()) - start;
    var progress = now / duration;
    if(progress > 1) progress = 1;
   
    var result = (to - that.from) * progress + that.from;
    that.domElement.css('left', result+'px');
    that.onGo.deliver(progress);
    if (progress < 1)
      setTimeout(arguments.callee, 10);
  }, 10);
}

...
var Panda=new Animal('panda');


* This source code was highlighted with Source Code Highlighter.


В конструкторе мы создали Издателя this.onGo=new Publisher(), а в методе сделали рассылку с текущим прогрессом that.onGo.deliver(progress).

Теперь осталось добавить наблюдателей для текущего состояния панды. Покажем прогрессбар и количество процентов пройденного пути. Очевидно, сделаем двух наблюдателя

var Status=function(v){

  var per=v*100;

  if(per>100)v=100;

  $("#status").html((per.toFixed(0))+"%")

}

var Bar=function(progress){

  var w=(progress*596);

  $("#progressbar").css('width',w+'px');

}

* This source code was highlighted with Source Code Highlighter.

И подпишем их на рассылку onGo

Bar.subscribe(Panda.onGo);

Status.subscribe(Panda.onGo);


* This source code was highlighted with Source Code Highlighter.


Теперь при движении панды изменяются и индикаторы.

В итоге мы получили три независимых объекта Bar, Status и Animal, которые можно использовать независимо друг от друга, а также возможность спокойно добавлять новые объекты, зависящие от состояния панды(издателя).

Похожего результата можно было бы достичь и с помощью jquery trigger/bind. Тогда бы вместо подписки, вешали бы bind. А в методе Animal.go добавляли trigger Пример с trigger/bind
Tags:
Hubs:
+38
Comments28

Articles