Typescript: как расширить два класса?
я хочу сэкономить свое время и повторно использовать общий код между классами, который расширяет классы PIXI (2D библиотека визуализации webGl).
Объект Интерфейсы:
module Game.Core {
export interface IObject {}
export interface IManagedObject extends IObject{
getKeyInManager(key: string): string;
setKeyInManager(key: string): IObject;
}
}
моя проблема в том, что код внутри getKeyInManager
и setKeyInManager
не изменится, и я хочу его повторно использовать, а не дублировать, вот реализация:
export class ObjectThatShouldAlsoBeExtended{
private _keyInManager: string;
public getKeyInManager(key: string): string{
return this._keyInManager;
}
public setKeyInManager(key: string): DisplayObject{
this._keyInManager = key;
return this;
}
}
то, что я хочу сделать, это автоматически добавить через Manager.add()
, ключ, используемый в менеджере для ссылки объект внутри сам объект в его собственность _keyInManager
.
Итак, давайте возьмем пример с текстурой. Вот идет TextureManager
module Game.Managers {
export class TextureManager extends Game.Managers.Manager {
public createFromLocalImage(name: string, relativePath: string): Game.Core.Texture{
return this.add(name, Game.Core.Texture.fromImage("/" + relativePath)).get(name);
}
}
}
когда я делаю this.add()
, Я хочу Game.Managers.Manager
add()
метод для вызова метода, который будет существовать на объект, возвращенный Game.Core.Texture.fromImage("/" + relativePath)
. Этот объект, в данном случае, будет Texture
:
module Game.Core {
// I must extends PIXI.Texture, but I need to inject the methods in IManagedObject.
export class Texture extends PIXI.Texture {
}
}
я знаю, что IManagedObject
является интерфейсом и не может содержать реализацию, но я не знаю, что делать напишите, чтобы ввести класс ObjectThatShouldAlsoBeExtended
внутри Texture
класса. Зная, что тот же самый процесс потребуется для Sprite
, TilingSprite
, Layer
и многое другое.
мне нужна опытная обратная связь/советы TypeScript здесь, это должно быть возможно сделать, но не несколькими расширениями, так как в то время возможен только один, я не нашел другого решения.
3 ответов
в TypeScript есть малоизвестная функция, которая позволяет использовать Mixins для создания повторно используемых небольших объектов. Вы можете составить их в более крупные объекты, используя множественное наследование (множественное наследование не разрешено для классов, но разрешено для mixins - которые похожи на интерфейсы с связанной имплененцией).
дополнительная информация о TypeScript Mixins
Я думаю, вы могли бы использовать этот метод для общих компонентов между многими классы в игре и повторно использовать многие из этих компонентов из одного класса в игре:
вот быстрая демонстрация Mixins... во-первых, ароматы, которые вы хотите смешать:
class CanEat {
public eat() {
alert('Munch Munch.');
}
}
class CanSleep {
sleep() {
alert('Zzzzzzz.');
}
}
затем волшебный метод для создания Mixin (вам нужно только один раз где-то в вашей программе...)
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}
и затем вы можете создавать классы с множественным наследованием от mixin flavours:
class Being implements CanEat, CanSleep {
eat: () => void;
sleep: () => void;
}
applyMixins (Being, [CanEat, CanSleep]);
обратите внимание, что в этом классе нет фактической реализации - как раз достаточно, чтобы он прошел требования "интерфейсов". Но когда мы используем этот класс-все работает.
var being = new Being();
// Zzzzzzz...
being.sleep();
Я бы предложил использовать новый подход mixins, описанный там: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/
этот подход лучше, чем подход "applyMixins", описанный Fenton, потому что автокомпилятор поможет вам и покажет все методы / свойства как из базового, так и из 2-го классов наследования.
этот подход может быть проверен на игровая площадка TS сайт.
вот реализация:
class MainClass {
testMainClass() {
alert("testMainClass");
}
}
const addSecondInheritance = (BaseClass: { new(...args) }) => {
return class extends BaseClass {
testSecondInheritance() {
alert("testSecondInheritance");
}
}
}
// Prepare the new class, which "inherits" 2 classes (MainClass and the cass declared in the addSecondInheritance method)
const SecondInheritanceClass = addSecondInheritance(MainClass);
// Create object from the new prepared class
const secondInheritanceObj = new SecondInheritanceClass();
secondInheritanceObj.testMainClass();
secondInheritanceObj.testSecondInheritance();
к сожалению, typescript не поддерживает множественное наследование. Поэтому нет абсолютно тривиального ответа, вам, вероятно, придется реструктурировать свою программу
вот несколько советов:
Если этот дополнительный класс содержит поведение, которое разделяют многие подклассы, имеет смысл вставить его в иерархию классов где-то вверху. Возможно, вы могли бы получить общий суперкласс Sprite, Texture, Layer,... из этого класс ? Это будет хорошим выбором, если вы можете найти хорошее место в иерархия типа. Но я бы не рекомендовал просто вставлять этот класс в случайную точку. Наследование выражает "является-отношением", например, собака является животным, текстура является экземпляром этого класса. Вы должны спросить себя, действительно ли это моделирует отношения между объектами в вашем коде. Логическое дерево наследования очень ценно
Если дополнительный класс не подходит логически в иерархии типов, можно использовать агрегацию. Это означает, что вы добавляете переменную экземпляра типа этого класса в общий суперкласс Sprite, Texture, Layer, ... Затем вы можете получить доступ к переменной с ее геттером / сеттером во всех подклассах. Это модель "имеет - отношение".
вы также можете преобразовать свой класс в интерфейс. Затем вы можете расширить интерфейс со всеми своими классами, но было бы правильно реализовать методы в каждом классе. Это означает некоторую избыточность кода, но в этом случае не так много.
вы должны решить для себя, какой подход вам больше нравится. Лично я бы рекомендовал преобразовать класс в интерфейс.
один совет: Typescript предлагает свойства, которые являются синтаксическим сахаром для геттеров и сеттеров. Возможно, вы захотите взглянуть на это: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/