1

I have a class, which depends on type of string, gets in in a swithc statement, and call to de new "class_name" related (constructor).

But, I would like to know if there is a better way to solve this.

It would be ideal if there is a way to call the builder independently, within a specific folder, of all the classes that exist.

Project example:

  • Types/_String.js
  • Types/_Date.js
  • Types/_Integer.js
  • .....
  • TypeSwitcher.js (contains the switch that calls the constructors of the "Types" classes).

Associated Code to "TypeSwitcher.js":

"use strict"
import _String from "./Inputs/_String";
import _Integer from "./Inputs/_Integer";
import _Date from "./Inputs/_Date";
import Document from "./Inputs/Document";
import Container from "./Inputs/Container";

export default class InputFactory
{
    static getInput(key,typedObject,form,inputDef)
    {
           let type=typedObject.getField(key).getTypeName();
           switch (type) 
           {
             case "String": {
               return new _String(key,typedObject,form,inputDef); 
             } break;

             case "Integer": {
               return new _Integer(key,typedObject,form,inputDef); 
             } break;

             case "Date": {
               return new _Date(key,typedObject,form,inputDef);                
             }break;

             case "Document": {
              return new Document(key,typedObject,form,inputDef);                
            }break;

            case "Container": {
              return new Container(key,typedObject,form,inputDef);                
            }break;  

            default: {throw "Input undefined:"+type}     
           }          
    }       
}

New (edited) I don't have classes only with an underscore "_", also normal. And I have put indent script since I am redefining the basic types of the system to customize them to my need (String.js is reserved and does not leave, so _String.js)

I tried to do something like that:

"use strict"
import _String from "./Inputs/_String";
import _Integer from "./Inputs/_Integer";
import _Date from "./Inputs/_Date";

const predefinedSystemVar = ["String", "Integer", "Date"];

export default class InputFactory
{
    static getInput (key, typedObject, form, inputDef)
    {
      let type = typedObject.getField(key).getTypeName();
      if (predefinedSystemVar.includes(type) )    
          type = `_${type}`;  
      return eval( `new types[type](key, typedObject, form, inputDef)` );          
    }
}

Updated But it didnot work well, because of webpack. v4 :( I think webpack v4 changes names to imports, etc., and it does not work the code trick ...

Problem, if I create a new class in the future, I would have to add a new "case" and import to contemplate the new constructor.

So, the solution I tried, I dont like so much, because I'd have to add new variables to "predefinedSystemVar"...

Is there something in ES6 or javascript to get this work in a better and optimized way instead of switch -> case?

Additional note: Project enviroment:

  • Webpack v.4.x
  • Babel 7.x
  • ES6

package.json

{
  "devDependencies": {
    "@babel/cli": "^7.7.0",
    "@babel/core": "^7.7.2",
    "@babel/plugin-proposal-class-properties": "^7.7.0",
    "@babel/preset-env": "^7.7.1",
    "babel-loader": "^8.0.6",
    "cypress": "^3.7.0",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.9.0"
  },

2 Answers2

1

Yes, if I am understanding this correctly it should be possible. The below code should work, unless I made some syntactical error. It does require the updating of a classes array, but is less work than updating the switch case and the other solution. I hope this helps and if it does please mark as the answer, have a good rest of your day, cheers!

"use strict"
import _String from "./Inputs/_String";
import _Integer from "./Inputs/_Integer";
import _Date from "./Inputs/_Date";

const classes = {
    _String,
    _Integer,
    _Date
};

export default class InputFactory
{
    static getInput(key,typedObject,form,inputDef)
    {
           let type=typedObject.getField(key).getTypeName();
           if (classes["_"+type]{
            return new classes["_"+type](key,typedObject,form,inputDef);
           } else {
            throw "Input undefined:"+type
           }         
    }       
}
herohamp
  • 615
  • 4
  • 13
  • Just drop the underscore from the names, and you won't have to prefix the property key. – Bergi Dec 23 '19 at 11:43
  • But if in the rest of his codebase hes using the underscores, its better to just add the underscores here than either rewrite the codebase or use different names here which could cause confusion and bugs – herohamp Dec 23 '19 at 11:44
  • Yes, could be a solution, but classes's name are mixed, with underscore and without them... I don't have classes only with an underscore "_", also normal. And I have put indent script since I am redefining the basic types of the system to customize them to my need (String.js is reserved and does not leave, so _String.js). – Habitaquo dev Dec 23 '19 at 11:52
  • 1
    You could always just change the array to be like so const classes = { "String": _String, "Integer": _Integer, "Date": _Date }; and remove the code that adds the underscore – herohamp Dec 23 '19 at 11:56
  • @HamptonMoore That's what I meant :-) What names the imports have (or whether Webpack modifies them) doesn't matter, only the property names of the object do. – Bergi Dec 23 '19 at 12:03
  • @Bergi exactly, with object: key: value, works "fine" with webpack, BUT you have to add manually new classes and imports.... It's not still "automatic" new Classes... – Habitaquo dev Dec 23 '19 at 12:13
  • Your answer is not completely understandable for other teammates who want to maintain this code in the future. – Ali Torki Dec 23 '19 at 12:34
  • What part? Any confusing parts should just be commented – herohamp Dec 23 '19 at 12:47
  • @Habitaquodev There is no "automatic" solution (apart from generated js files) – Bergi Dec 23 '19 at 13:18
  • @Bergi really?? what a pity! I imagined it... There is a proposal for javascript about dynamic import: https://github.com/tc39/proposal-dynamic-import – Habitaquo dev Dec 23 '19 at 14:42
  • @Habitaquodev Sure, it's always possible to come up with something involving `readdir`, `eval`, dynamic `import` or global variables, but still: the best practice is to *explicitly* list the classes that you want to make available by name. – Bergi Dec 23 '19 at 14:46
  • @Bergi Yeah, I supposed that´s the best practise... BTW, I hope the develop this feature ASAP... Thanks for everything! – Habitaquo dev Dec 23 '19 at 16:34
-1

A way is create an object which keys are the same as cases and values are a function that returns a value as you desire as below:

"use strict"
import _String from "./Inputs/_String";
import _Integer from "./Inputs/_Integer";
import _Date from "./Inputs/_Date";

export default class InputFactory {
    static getInput(key, typedObject, form, inputDef) {
        let type = typedObject.getField(key).getTypeName();
        const operations = {
            "String": () => new _String(key, typedObject, form, inputDef),
            "Integer": () => new _Integer(key, typedObject, form, inputDef),
            "Date": () => new _Date(key, typedObject, form, inputDef),
        }

        try {
            return operations[type]();
        } catch() {
            throw "Input undefined:" + type
        }
    }
}
Ali Torki
  • 1,929
  • 16
  • 26