Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js - create object of class name specified in variable

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?

like image 580
Forseti Avatar asked Dec 18 '17 10:12

Forseti


4 Answers

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

like image 127
scniro Avatar answered Nov 01 '22 15:11

scniro


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]();
like image 43
GaloisGecko Avatar answered Nov 01 '22 17:11

GaloisGecko


  1. 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 + '()');
    
  2. 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;
    }
    
  3. Finally, the best and wise decision is to apply Factory pattern. See @scniro answer. Also you can find good description and example here

like image 4
oxfn Avatar answered Nov 01 '22 16:11

oxfn


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 requires 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.

like image 2
Forseti Avatar answered Nov 01 '22 17:11

Forseti