Простой способ создать экземпляр объекта с именем класса Dynami c? - PullRequest
0 голосов
/ 13 июля 2020

Есть ли простой способ создать экземпляр объекта, когда имя класса, который вы хотите создать, находится в переменной? Я переношу существующее приложение на TS / Node.js, и у меня есть абстрактный базовый класс с фабрикой классов, где я передаю тип класса, который мне нужен, в качестве параметра, который сам поступает из среды.

Я хотел бы пока сохранить код как можно ближе к оригиналу, но я не вижу простого способа сделать это, который не требует от меня, например, изменения базового класса каждый раз, когда я добавляю новый дочерний класс, который наследуется от него.

Урезанная версия кода, который я хочу, в основном выглядит так:

export abstract class Foo {
  static async get(className : string) : FOO {
    if (!FOO._instance) {
      FOO._instance = new className();
    }
    return FOO._instance;
  }
}

... then ...

export class Bar extends Foo {
  /* stuff */
}

... and finally used as ...

const baz = await Foo.get('Bar');
// baz is now an instance of Bar.

Реальный код сложнее этого, но все работает, кроме строки содержащий new className(), и я не могу найти способ передать имя класса, которое я хочу создать, в new или какой-либо другой способ создания экземпляра класса. PHP, Java, Perl и C# - все делают это довольно просто, но я не могу найти аналога в TS. Самое близкое, что я придумал, - это, например, оператор switch в фабричном геттере, который знает обо всех подклассах, который будет работать, но не совсем оптимален.

TIA.

1 Ответ

1 голос
/ 13 июля 2020

Вы можете зарегистрировать дочерние классы в абстрактном базовом классе, который сопоставляет имя класса с фактическим классом:

export abstract class Foo {
  static _classMapping: { [key: string]: { new(): Foo }} = {}
  static _instance : Foo | null = null
  
  static get(className : string) : Foo | null {
    if (!Foo._instance) {
      Foo._instance = new (this._classMapping[className])();
    }
    return Foo._instance;
  }

  static _register(className : string, classRef : { new(): Foo }) {
    Foo._classMapping[className] = classRef;
  }
}

// ... then ...

export class Bar extends Foo {
  /* stuff */
}

Foo._register('Bar', Bar)

// ... and finally used as ...

const baz = await Foo.get('Bar');
console.log(baz.constructor.name)
// baz is now an instance of Bar.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...