J'ai une hiérarchie de classe comme:
|-> Square
AbstractShape -+-> Circle
|-> Triangle
Maintenant, j'aimerais implémenter un modèle de stratégie et créer un objet de classe stocké dans une chaîne. Dans PHP, j'utiliserais:
$type = 'Square';
$obj = new $type();
Existe-t-il un équivalent dans Node.js?
Si vous souhaitez adopter une approche plus robuste et testable, vous pouvez utiliser une combinaison de classes et un modèle d'usine pour émettre l'objet. Découvrez ce qui suit: vous verrez que cette configuration, y compris une logique plus granulaire et des tests ultérieurs, serait plus facile et vous offrirait une plus grande flexibilité. Vous êtes également en train de chercher l'objet vous-même derrière l'appel .issue
- ce qui peut être bénéfique et pratique dans certains cas.
Je remarque également que vous mentionnez votre arrière-plan PHP. Je montre donc également comment une approche orientée objet peut être adoptée dans 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 - démo
De plus, si vous souhaitez quelque chose de plus tolérant aux pannes que le traitement de chaînes redondantes, par exemple. 'Square'
- Il existe des moyens créatifs de tirer parti d’approches analogues à celles de l’énumération pour affiner encore davantage cette approche. Je vais sauvegarder les biens immobiliers ici et ne pas re-hacher l'extrait de code, mais inclure un violon que vous pourrez vérifier.
JSFiddle Link - Démo de l'approche enum
Un moyen sûr serait de définir un objet d'usine:
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]();
La manière rapide et sale consiste à utiliser eval
. Mais il est fortement déconseillé pour plusieurs raisons - sécurité, performance, lisibilité, supportabilité
function MyType() {
}
var typeName = 'MyType';
var typeObj = eval('new ' + typeName + '()');
Il est beaucoup plus sûr et correct que eval
d’utiliser le mappage des noms de chaînes en types (grâce à @GaloisGecko)
function createType(name) {
var types = {
"Square": Square,
"Circle": Circle,
"Triangle": Tringle
};
return types.hasOwnProperty(name) ? new types[name]() : null;
}
Enfin, la meilleure et sage décision consiste à appliquer le motif Factory. Voir @scniro answer . Aussi, vous pouvez trouver une bonne description et exemple ici
En y regardant de plus près, il existe un moyen assez simple de contourner Node.js. Lorsque vous instanciez un objet de la manière la plus simple, vous écrivez réellement new <variableName>
où variableName
est un corps d’une fonction ou d’une classe définie et exportée dans un module. Pour affecter cette fonction/classe à la variable, vous require()
it.
Donc, au lieu de
const type = 'Square';
const aSquare = new type();
vous devez écrire:
const type = 'Square';
const shape = require(`${pathToShapeModules}/${type}.js`);
const aShape = new shape();
L'inconvénient mineur est qu'eslint se plaint (dans certains paramètres de règle) que require
s doit être placé sur le module. Et bien sûr, il faut gérer correctement les exceptions en essayant… attraper etc., donc probablement la solution Factory est meilleure (donc je vais l'accepter), mais je pense que pour les petits cas spécialisés, cette solution est acceptable.