2012-09-06 10 views
13

Hola estoy tyring para implementar patrón de observador en JavaScript:cómo implementar el patrón de observador en javascript?

Mis 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]); 
}); 

mi película:

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

Por último observador:

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

Me agradecerán cualquier ayuda que pueda brindar.

Respuesta

12

En JavaScript, no tiene sentido implementar un patrón de observador puro como en Java, porque JavaScript tiene esta pequeña cosa llamada programación funcional. Así que solo use algo como http://api.jquery.com/category/callbacks-object/ en lugar de su ObserverList.

Si aún desea usar su objeto, todo dependerá de lo que quiera pasar a ObserverList.Add. Si se trata de un objeto, entonces usted necesita para escribir

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

si se trata de una función, entonces usted necesita para escribir

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

También puede utilizar Function.apply() o Function.call() para suministrar this a su función

+0

Voy a comprobar hacia fuera, pero si Quería hacerlo funcionar tal como es, ¿qué debo hacer? –

+1

Actualizado la respuesta –

+0

HI Gracias Dmitry, ¿dónde debo hacer el complemento? y cual deberia usar? ¿el que está en la clase de observador o en la clase de película? muchas gracias –

28

JavasScript es orientada a eventos: Eso significa que es consciente del tiempo y espera que las cosas cambian con el tiempo. El Patrón de Observer original se creó para lenguajes como C++ que no tienen conocimiento del tiempo. Puede aprovechar las fortalezas de JavaScript mediante el uso de un bucle de juego para verificar los cambios de estado.

Cree dos elementos DOM, una entrada y salida de

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

Establecer un bucle requestAnimationFrame y comenzar a observar.

//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(); 

Esto es lo que hacen los motores de juego de modo inmediato prestación. También es lo que hace el marco React para verificar los cambios de estado en el DOM.

(Si lo necesita, he aquí una sencilla 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); 
      }; 
})(); 
3

Aquí es una implementación del patrón Observer en JavaScript que proporciona una API muy similar a Backbone Models. Esta implementación evita el uso de "esto" y "nuevo", como suggested by 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; 
     }); 
     } 
    } 
    }; 
} 

Aquí hay un código de ejemplo que utiliza Modelo:

// 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" 
+0

Este es el patrón literal del objeto. Para el patrón revelador del módulo, ambas funciones se obtienen, el conjunto se define en el cierre y se devuelve un objeto al final del formulario 'return {get: get, set: set};' –

+0

Su solución es más una Publicación - patrón de suscripción https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern –

1

Para mí esta es la mejor manera de implementar un patrón de observador en 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(); 
} 
+0

¿Qué ocurre si se adjunta más de un oyente con la misma funcionalidad? digamos que métodoA y métodoB suscriben la misma función. – Dementic

0

A continuación se muestra una implementación me adapto una poco del libro 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 

¡Salud!

0

El patrón de observador consiste en actualizar un objeto y que esas actualizaciones envíen automáticamente un evento que brinde información sobre lo que se actualizó.

Por ejemplo:

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

Aquí es un módulo del patrón de observador en javascript, puede echar un vistazo al código fuente Para mayor información: https://github.com/Tixit/observe

Cuestiones relacionadas