こんにちは私はJavaScriptでオブザーバーパターンを実装するために疲れています。
私の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]);
});
私の映画:
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);
};
最後にオブザーバー:
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;
};
あなたが提供できるどんな助けでも私は最も感謝します。
JavaScriptには、Javaのように純粋なオブザーバーパターンを実装する意味はありません。JavaScriptには関数型プログラミングと呼ばれる小さなものがあるためです。したがって、ObserverListの代わりに http://api.jquery.com/category/callbacks-object/ のようなものを使用してください。
それでもオブジェクトを使用したい場合、すべてはObserverList.Addに何を渡したいかに依存します。それが何らかのオブジェクトである場合、あなたは書く必要があります
for( i = 0; i < observers.Count; i++) {
observers[i].Notify("some data");
}
それが関数である場合、あなたは書く必要があります
for( i = 0; i < observers.Count; i++) {
observers[i]("Some data");
}
また、Function.apply()またはFunction.call()を使用して、this
を関数に提供することもできます
JavasScriptはevent-drivenです。つまり、時間を認識しており、時間の経過とともに物事が変化することを期待しています。元のObserverパターンは、時間を認識しないC++などの言語用に作成されました。 game loopを使用してJavaScriptの強みを活用し、状態の変化を確認できます。
入力と出力の2つのDOM要素を作成する
<input type="text" value="Enter some text...">
<p id="output">
requestAnimationFrame
ループを設定して、監視を開始します。
//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();
これは、即時モードレンダリングのためにゲームエンジンが行うことです。 ReactフレームワークがDOMの状態変化をチェックするために行うことでもあります。
(必要な場合は、単純な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);
};
})();
以下は、JavaScriptのオブザーバーパターンの実装で、 Backbone Models によく似たAPIを提供します。 Douglas Crockfordにより提案 のように、この実装では「this」と「new」の使用を回避します。
// 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;
});
}
}
};
}
次に、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"
class EventObserver {
constructor () {
this.observers = []
}
subscribe (fn) {
this.observers.Push(fn)
}
unsubscribe (fn) {
this.observers = this.observers.filter(subscriber => subscriber !== fn)
}
broadcast (data) {
this.observers.forEach(subscriber => subscriber(data))
}
}
または、NodeJでEventEmitterを使用できます https://nodejs.org/api/events.html#events_class_eventemitter
以下は、書籍「Javascriptデザインパターンの学習」から少し変更した実装です。
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
乾杯!
私にとってこれは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();
}
これは古いですが、「既存のコードでオブザーバーパターンを実装する方法」という元の質問への回答を提供したいと思いました。
オブザーバーパターンは、ターゲット(監視対象)がオブザーバーへのポインターを持ち、オブザーバーのパブリックAPIを想定する通信設計として簡略化できます。たとえば、ターゲットは、オブザーバーがupdate
というメソッドを持っているか、オブザーバーがFunction
であると想定しています。これは、オブザーバーオブジェクト(またはオブザーバーが関数の場合はFunction
)のメソッドを実際に呼び出すことにより、ターゲットがオブザーバーに変更を通知する方法です。
プロパティが変更または変更されるたびに、ターゲットは通知を受けるように登録されているすべてのオブザーバーをupdate
する必要があります。
ここでは、Key-Value Observerの実装方法を知りたいと想定しています。その場合、コードはそのオブザーバーリストを反復処理し、プロパティが変更されたときに各オブザーバーのupdate
メソッドを呼び出す(またはFunction
の場合はオブザーバーを実行する)必要があります。
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)
}
そのchanged
メソッドを追加し、すべてのセッターメソッドを更新して、上記のように変更を呼び出す必要があります。
また、元のコードには映画をobserves
する場所がないことに気づきました。上記の例では、実際にobserves
を実装するコード、update
を実装するコードを追加する必要があります。
play
とstop
の意図がわからない。
オブザーバーパターンは、オブジェクトの更新に関するものであり、それらの更新により、何が更新されたかに関する情報を提供するイベントを自動的に送信します。
例えば:
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)
}
これはJavaScriptのオブザーバーパターンのモジュールです。詳細については、ソースコードをご覧ください。 https://github.com/Tixit/observe