I have a class hierarchy like:
|-> Square
AbstractShape -+-> Circle
|-> Triangle
Now, I'd like to implement strategy pattern and create an object of class that's stored in string. In PHP I'd use:
$type = 'Square';
$obj = new $type();
Is there an equivalent in Node.js?
If you're wishing to go with a more robust and testable approach, you could use a combination of classes and a factory pattern to issue the object. Check out the following, you'll see that with this setup, including more granular logic and testing down the road would come easier and afford you greater flexibility. You're also abstracting away new-ing up the object yourself behind the .issue
call - which can be beneficial and convenient in some cases.
I also notice that you mention your PHP background, so I'm also showcasing a bit of how an object orientated approach can be taken in ES6.
class AbstractShape {
constructor(type) {
this.type = type;
}
getType() {
console.log(`I am a ${this.type}`);
}
}
class Square extends AbstractShape {
constructor(type) {
super(type);
this.sides = 4;
}
getDescription() {
console.log(`I have ${this.sides} equal sides`);
}
}
class ShapeFactory {
static issue(type) {
switch(type) {
case 'Square': return new Square(type);
break;
case 'Circle': /* same pattern with a Circle class */
break;
}
}
}
let shape = ShapeFactory.issue('Square');
shape.getType(); /* I am a Square */
shape.getDescription(); /* I have 4 equal sides */
JSFiddle Link - demo
Furthermore, if you'd like something a bit more fault tolerant than dealing with redundant strings e.g. 'Square'
- there are some creative ways to leverage enum-like approaches that could refine this even further. I'll save the real estate here and not re-hash the code snippet, but will include a fiddle for you to check out.
JSFiddle Link - enum approach demo
A safe way would be defining a factory object:
function Square() {
}
// here other constructors for Circle and Triangle
var factory = {
"Square": Square,
"Circle": Circle,
"Triangle" : Triangle
}
var typeName;
// here some code which sets typeName
var typeObj = new factory[typeName]();
The quick and dirty way is to use eval
. But it is strongly not recommended because of many reasons - security, performance, readability, supportability
function MyType() {
}
var typeName = 'MyType';
var typeObj = eval('new ' + typeName + '()');
Much safer and more correct than eval
is to use mapping of string names to types (thanks to @GaloisGecko)
function createType(name) {
var types = {
"Square": Square,
"Circle": Circle,
"Triangle": Tringle
};
return types.hasOwnProperty(name) ? new types[name]() : null;
}
Finally, the best and wise decision is to apply Factory pattern. See @scniro answer. Also you can find good description and example here
Upon closer consideration there is quite simple way around for Node.js.
When you instantiate an object in simplest way you actually write new <variableName>
where variableName
is a body of some function or class defined and exported in some module. To assign this function/class to variable you require()
it.
So, instead of
const type = 'Square';
const aSquare = new type();
you need to write:
const type = 'Square';
const shape = require(`${pathToShapeModules}/${type}.js`);
const aShape = new shape();
Minor downside is that eslint complains (in some rule settings) that require
s are to be placed on top of the module. And of course it needs proper exception handling by try...catch etc. so probably Factory solution is better (so I'm going to accept it) but I think that for small specialized cases this solution is ok.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With