I am developing a server-side JavaScript tool, and trying to do so in a modular way.
I have a Parser
class that is an EventEmitter
- you call parse
and it will eventually emit data
and end
events. It is generally invoked like so:
var parser = new Parser(options);
parser
.on('data', handleData)
.on('end', handleEnd)
.parse();
Now I want to extend this Parser in a way that imposes some sort of state - rather than simply emitting data
for every parse result, it tracks how the results change between parses. I have built another class called Stateful
that accepts a Parser
instance and is built around it (composition-style). It is an EventEmitter
itself and emits insert
, update
, delete
, and end
. You use it like so:
var parser = new Stateful(new Parser(options));
parser
.on(/* handle events */)
.parse();
Now I want to extend this functionality further. In addition to tracking state, I want to track the order of these parse results according to some pre-defined sorting algorithm, as each parse result is emitted. This does have a performance hit over the Stateful
parser, and there would be reason to use Stateful
with tracking sort order, so I want to keep it separate. It will emit the same events as Stateful
, but they will have some additional objects passed to their handlers.
So what is the best way to do this?
I could further nest composition, like so:
var parser = new Ordered(new Stateful(new Parser(options)));
But that seems smelly to me. Ordered
relies upon being passed a Stateful
and would break if it was just passed a Parser
.
Alternatively, I could say that Ordered
inherits from Stateful
, but remember that Stateful
communicates with events, which do not like to behave polymorphically. Take this example below. I used ES6 class pseudo-syntax for terseness, let me know if it is unclear at all. I am actually writing this in ES5.
class Stateful extends EventEmitter {
constructor(parser) {
this.parser = parser;
parser
.on('data', this.handleData.bind(this))
.on('end', this.handleEnd.bind(this));
}
handleData(data) {
if (isNew(data)) {
this.emit('insert', data);
} else {
this.emit('update', data);
}
}
handleEnd() {
for (data in dataNotYetHandled()) {
this.emit('delete', data);
}
this.emit('end');
}
}
class Ordered extends Stateful {
/* ??? */
}
The handleData
and handleEnd
methods emit events directly, so I cannot make use of them directly in the superclass implementation. I could separate the logic from the emit
calls, but that would break encapsulation, requiring my subclass to make use of calls outside of its superclass's public interface.
What pattern would be best here? I am in control of all 3 classes and could make changes to the interfaces of any of them. Please let me know if I could provide any more info. Thanks.