I currently have the problem that I have an object which should listen on another object.
The question is: how should I handle the subscription? Currently, I only know o
It really depends on how the objects will broadcast and listen and how coupled or decoupled you require them to be. Generally speaking, the best use of this type of event architecture is where an object can indiscriminately announce events to other objects and those objects can choose to listen (register) or stop listening (unregister) at their own discretion.
It might make sense in some scenarios to make the listener a dependent of the broadcaster, in which case it will be strongly coupled to the event name and the arguments of the event being announced by that broadcaster.
var Listener = function(broadcaster) {
var self = this;
broadcaster.on("event", function(info) {
self.doSomething(info);
});
};
Other listener classes can also be created and register themselves with the same broadcaster. The broadcaster has no knowledge of the listeners only the listener is aware of the relationship.
The broadcaster announces events only for the listener. In this scenario the event architecture may be overkill, you could replace the events with direct calls. Your second example hints at this because of the one to one relationship with the listener in the broadcaster's constructor.
var Broadcaster = function(listener) {
this.doSomething = function() {
// do something...
listener.doSomething(info);
};
};
This creates strong coupling between the broadcaster and the interface for the listener. This pattern is not as limiting as it may first appear though. A common approach to extending the usefulness of this pattern is to replace the basic listener with a broadcasting adapter. The broadcasting adapter appears the same as the basic listener in that it carries the same doSomething
interface but implements an event architecture to pass this call on to other objects. This keeps the broadcaster very simple and externalizes anything to do with events at the cost of an additional class.
Your first example is a good example of releasing this coupling the cost is an additional class. The intermediate class acts as a bridge or adapter between the two classes so that neither is dependent on each other's information. The event signature is hidden from the listener and the method signature for the listener is hidden from the broadcaster. Often this level of decoupling is unnecessary but is an important tool to be aware of where it is important to keep two classes isolated from each other.
var Bridge = function(listener, broadcaster) {
broadcaster.on("event", function(info) {
// if necessary translate the arguments as well
listener.doSomething(info);
});
};
Per your comment, I'm not sure what you find unreadable about this, but you can try using Stream objects and the elegant Stream#pipe method.
var Stream = require('stream').Stream;
var util = require('util');
var Sender = function() {
this.readable = true;
this.saySomething = function(message) {
this.emit('data', message);
};
};
util.inherits(Sender, Stream);
var Receiver = function() {
this.writable = true;
this.write = function(message) {
console.log('received: ' + message);
}
};
util.inherits(Receiver, Stream);
// instance of each
var s = new Sender();
var r = new Receiver();
// connect using pipe!
s.pipe(r);
// an event
s.saySomething('hello world'); //=> received: hello world
I particularly like using Stream
objects. The pipe
method is quite handy :) Each stream has a single input and output. I like them because it encourages you to build small, functional components that are responsible for a specific task. Then, you can chain/reuse them however you see fit.
You'll want to use EventEmitter
Here's a quick example
var events = require('events');
var util = require('util');
var SampleEmitter = function() {
events.EventEmitter.call(this);
this.sayHello = function() {
this.emit('hello', 'hello world');
};
};
util.inherits(SampleEmitter, events.EventEmitter);
var SampleListener = function(emitter) {
emitter.on('hello', function(data) {
console.log(data);
});
};
var se = new SampleEmitter();
var sl = new SampleListener(se);
se.sayHello(); //=> 'hello world'