web-dev-qa-db-fra.com

comment implémenter un motif d'observateur en javascript?

Bonjour, je tente de mettre en place un modèle d'observateur en JavaScript:

Mon index.js :

$(document).ready(function () {
  var ironMan = new Movie();
  ironMan.setTitle('IronMan');
  ironMan.setRating('R');
  ironMan.setId(1);
  //  ironMan.setCast(['Robert Downey Jr.', 'Jeff Bridges', 'Gwyneth Paltrow']);

  var terminator = new Movie();
  terminator.setTitle('Terminator');
  terminator.setRating('P');
  terminator.setId(2);

  console.log(ironMan.toString());
  console.log(terminator.toString());

  ironMan.play();
  ironMan.stop();
  ironMan.download();
  ironMan.share('V. Rivas');

  console.log(ironMan.getCast()[0]);
});

Mon film :

var title;
var rating;
var id;
var observers;


function Movie() {
  observers = new ObserverList();
}

//function Movie (title, rating, id){
//  this. title = title;
//  this.rating =  rating;
//  this.id =id;
//  observers = new ObserverList();
//}

Movie.prototype.setTitle = function (newTitle) {
  this.title = newTitle;
}

Movie.prototype.getTilte = function () {
  return this.title;
}

Movie.prototype.setRating = function (newRating) {
  this.rating = newRating;
}

Movie.prototype.getRating = function () {
  return this.rating;
}

Movie.prototype.setId = function (newId) {
  this.id = newId;
}

Movie.prototype.getId = function () {
  return this.id;
}

Movie.prototype.play = function () {
  for (i = 0; i < observers.Count; i++) {
    console.log("palying...");
  }
}

Movie.prototype.stop = function () {
  for (i = 0; i < observers.Count; i++) {
    console.log("stoped");
  }
}

Movie.prototype.AddObserver = function (observer) {
  observers.Add(observer);
};

Enfin observateur :

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;
};

Toute aide que vous pourrez fournir me sera très reconnaissante.

14
Ignacio Garat

En JavaScript, il est inutile de mettre en oeuvre un modèle d'observateur pur comme en Java, car JavaScript a cette petite chose appelée programmation fonctionnelle. Utilisez donc quelque chose comme http://api.jquery.com/category/callbacks-object/ à la place de ObserverList.

Si vous souhaitez toujours utiliser votre objet, tout dépend de ce que vous souhaitez transmettre à ObserverList.Add. Si c'est un objet, alors vous devez écrire

for( i = 0; i < observers.Count; i++) { 
  observers[i].Notify("some data"); 
}

Si c'est une fonction, vous devez écrire

for( i = 0; i < observers.Count; i++) { 
  observers[i]("Some data"); 
}

Vous pouvez aussi utiliser Function.apply () ou Function.call () pour fournir this à votre fonction.

12
Dmitry Osinovskiy

JavasScript est event-driven : Cela signifie qu’il est conscient du temps et attend que les choses changent avec le temps. Le modèle Observer d'origine a été créé pour des langages tels que C++ qui ne sont pas conscients de l'heure. Vous pouvez exploiter les forces de JavaScript en utilisant un boucle de jeu _ pour vérifier les changements d'état.

Créer deux éléments DOM, une entrée et une sortie

<input type="text" value="Enter some text...">
<p id="output">

Configurez une boucle requestAnimationFrame et commencez à observer.

//Get a reference to the input and output
var input = document.querySelector("input");
var output = document.querySelector("#output");

//Set up a requestAnimationFrame loop
function update () {
  requestAnimationFrame(update);

  //Change the output to match the input
  output.innerHTML = input.value;
}
update(); 

C’est ce que les moteurs de jeu font pour mode immédiat rendu. C'est également ce que fait le framework React pour vérifier les changements d'état dans le DOM.

(Si vous en avez besoin, voici un simple requestAnimationPolyfill)

//Polyfill for requestAnimationFrame
window.requestAnimationFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          window.oRequestAnimationFrame      ||
          window.msRequestAnimationFrame     ||
          function(/* function */ callback, /* DOMElement */ element){
            window.setTimeout(callback, 1000 / 60);
          };
})();
28
d13

Voici une implémentation du modèle Observer en JavaScript qui fournit une API très similaire à Backbone Models . Cette mise en œuvre évite l'utilisation de "ceci" et "nouveau", comme le suggère suggéré par Douglas Crockford .

// The constructor function.
function Model(){

  // An object containing callback functions.
  //  * Keys are property names
  //  * Values are arrays of callback functions
  var callbacks = {},

      // An object containing property values.
      //  * Keys are property names
      //  * Values are values set on the model
      values = {};

  // Return the public Model API,
  // using the revealing module pattern.
  return {

    // Gets a value from the model.
    get: function(key){
      return values[key];
    },

    // Sets a value on the model and
    // invokes callbacks added for the property,
    // passing the new value into the callback.
    set: function(key, value){
      values[key] = value;
      if(callbacks[key]){
        callbacks[key].forEach(function (callback) {
          callback(value);
        });
      }
    },

    // Adds a callback that will listen for changes
    // to the specified property.
    on: function(key, callbackToAdd){
      if(!callbacks[key]){
        callbacks[key] = [];
      }
      callbacks[key].Push(callbackToAdd);
    },

    // Removes a callback that listening for changes
    // to the specified property.
    off: function(key, callbackToRemove){
      if(callbacks[key]){
        callbacks[key] = callbacks[key].filter(function (callback) {
          return callback !== callbackToRemove;
        });
      }
    }
  };
}

Voici un exemple de code utilisant Model:

// Create a new model.
var model = Model();

// Create callbacks for X and Y properties.
function listenX(x){
  // The new value is passed to the callback.
  console.log('x changed to ' + x);
}

function listenY(y){
  // The new value can be extracted from the model.
  console.log('y changed to ' + model.get('y'));
}

// Add callbacks as observers to the model.
model.on('x', listenX);
model.on('y', listenY);

// Set values of X and Y.
model.set('x', 30); // prints "x changed to 30"
model.set('y', 40); // prints "y changed to 40"

// Remove one listener.
model.off('x', listenX);
model.set('x', 360); // prints nothing
model.set('y', 50); // prints "y changed to 40"
3
curran

Pour moi, c’est le meilleur moyen de mettre en oeuvre un motif Observer dans JS.

function Click() {
    this.handlers = [];  // observers
}

Click.prototype = {

    subscribe: function(fn) {
        this.handlers.Push(fn);
    },

    unsubscribe: function(fn) {
        this.handlers = this.handlers.filter(
            function(item) {
                if (item !== fn) {
                    return item;
                }
            }
        );
    },

    fire: function(o, thisObj) {
        var scope = thisObj || window;
        this.handlers.forEach(function(item) {
            item.call(scope, o);
        });
    }
}

// log helper

var log = (function() {
    var log = "";

    return {
        add: function(msg) { log += msg + "\n"; },
        show: function() { alert(log); log = ""; }
    }
})();

function run() {

    var clickHandler = function(item) { 
        log.add("fired: " + item); 
    };

    var click = new Click();

    click.subscribe(clickHandler);
    click.fire('event #1');
    click.unsubscribe(clickHandler);
    click.fire('event #2');
    click.subscribe(clickHandler);
    click.fire('event #3');

    log.show();
}
1
GutiMac

Vous trouverez ci-dessous une implémentation que j'ai un peu adaptée du livre Learning Javascript Design Patterns.

function pubsub(obj) {

var events = {},
    subUid = -1;

obj.publish = function (event, args) {

    if (!events[event]) {
        return false;
    }

    var subscribers = events[event],
    len = subscribers ? subscribers.length : 0;

    while (len--) {
        subscribers[len].func(event, args);
    }
};

obj.subscribe = function (event, func) {

    if (!events[event]) {
        events[event] = [];
    }

    var token = (++subUid).toString();
    events[event].Push({
        token: token,
        func: func
    });

    return token;

};

obj.unsubscribe = function (token) {

    for (var event in events) {
        if (events.hasOwnProperty(event)) {
            for (var i = 0, j = events[event].length ; i < j ; i++) {
                if (events[event][i].token === token) {
                    events[event].splice(i, 1);
                }
            }
        }
    }

    return this;

};

}

var obj = {}; // Any javascript object
pubsub(obj); // Make an observable from the given object

var subscription = obj.subscribe('load', handler);

// event handler callback
function handler(event, data) {
    console.log(event, data);
}

obj.publish('load', 'Data loaded successfully'); // logs 'load Data loaded successfully'
obj.unsubscribe(subscription);
obj.publish('load', 'Data loaded successfully'); // nothing happens

À votre santé!

1
zak.http

C'est vieux, mais je voulais apporter une réponse à la question initiale, "comment implémenter le modèle d'observateur compte tenu du code existant".

Le modèle Observateur peut être simplifié en tant que conception de communication dans laquelle la cible (la chose observée) a un pointeur sur le ou les observateurs et suppose une API publique pour un observateur. Par exemple, la cible suppose qu'un observateur dispose d'une méthode appelée update ou qu'un observateur est une Function. C'est comment une cible informe les observateurs des modifications, en appelant une méthode sur l'objet observateur (ou Function si l'observateur est une fonction).

Chaque fois qu'une propriété est mutée ou modifiée, la cible doit update tous les observateurs enregistrés pour être avertis.

Ici, je suppose que vous voulez savoir comment implémenter un observateur Key-Value. Dans ce cas, le code devra parcourir la liste de ses observateurs et appeler chaque méthode update de chaque observateur (ou simplement exécuter l'observateur dans le cas où il s'agit d'une Function) lorsqu'une propriété est modifiée.

var observers = null;

function Movie() {
 observers = new ObserverList();
}

Movie.prototype.changed = function(key, old, value){
  // Assumption here is that observers can observe individual properties.
  if(!this.observers[key]) return
  
  this.observers[key].forEach( o => {
    // Assumption is that observers have an update method. This is the only
    // thing the target knows about an observer.
      o.update(key, old, value, this)
  })

}

// Now every setter on the target has to notify the observers by calling `changed`
Movie.prototype.setTitle = function (newTitle) {
  var old = this.title;
  this.title = newTitle;
  this.changed("title", old, this.title, this)
}

vous devez ajouter cette méthode changed, puis mettre à jour toutes les méthodes de configuration à appeler modifiées comme indiqué ci-dessus.

J'ai aussi remarqué qu'il n'y a nulle part dans le code original que observes du film. Vous devrez ajouter du code qui observes le fait, quelque chose qui implémente update, à partir de l'exemple ci-dessus.

Je ne suis pas sûr de l'intention de play et stop.

0
Joey Guerra

Le modèle d'observateur consiste à mettre à jour un objet et à ce que ces mises à jour envoient automatiquement un événement qui donne des informations sur ce qui a été mis à jour. 

Par exemple:

function ObserverList(){
  this.observerList = []
  this.listeners = []
}

ObserverList.prototype.add = function( obj ){
  this.observerList.Push(obj)
  this.listeners.forEach(function(callback){
    callback({type:'add', obj:obj})
  })
}

ObserverList.prototype.onChange = function(callback){
  this.listeners.Push(callback)
}

Voici un module du modèle observer en javascript, vous pouvez consulter le code source pour plus d'informations: https://github.com/Tixit/observe

0
B T