如何扩展两个类?

我想节省我的时间和重用通用代码跨类扩展 PIXI 类(一个2d webGl 渲染器库)。

对象接口:

module Game.Core {
export interface IObject {}


export interface IManagedObject extends IObject{
getKeyInManager(key: string): string;
setKeyInManager(key: string): IObject;
}
}

我的问题是 getKeyInManagersetKeyInManager中的代码不会改变,我想重用它,而不是复制它,下面是实现:

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 extend PIXI.Texture, but I need to inject the methods in IManagedObject.
export class Texture extends PIXI.Texture {


}
}

我知道 IManagedObject是一个接口,不能包含实现,但是我不知道如何在 Texture类中注入类 ObjectThatShouldAlsoBeExtended。知道同样的过程将需要为 SpriteTilingSpriteLayer和更多。

我需要有经验的 TypeScript 反馈/建议在这里,它必须是可能做到这一点,但不是由多个扩展,因为只有一个可能的时候,我没有找到任何其他解决方案。

200212 次浏览

遗憾的是,type escript 不支持多重继承。因此,没有完全平凡的答案,您可能必须重新构造您的程序

以下是一些建议:

  • 如果这个附加类包含许多子类共享的行为,那么将其插入到类层次结构的顶部某个位置是有意义的。也许你可以从这个类派生出精灵,纹理,图层,... 的公共超类?如果您能够在类型层次结构中找到一个好位置,那么这将是一个很好的选择。但是我不建议随机插入这个类。继承表示“ Is a-relations”,例如,狗是动物,纹理是这个类的实例。您必须扪心自问,这是否真的建模了代码中对象之间的关系。逻辑继承树非常有价值

  • 如果附加类在逻辑上不适合类型层次结构,则可以使用聚合。这意味着你可以将这个类的实例变量添加到一个公共的超类 Sprite,Texture,Layer,... 然后你就可以在所有的子类中使用它的 getter/setter 来访问这个变量。这是一种“有一段关系”的模式。

  • 还可以将类转换为接口。然后可以用所有类扩展接口,但必须在每个类中正确实现方法。这意味着一些代码冗余,但在本例中并不多。

你必须自己决定你最喜欢哪种方法。我个人建议将类转换为接口。

一个提示: Type 脚本提供了属性,这些属性是 getter 和 setter 的语法糖。你可能想看看这个: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/

TypeScript 中有一个鲜为人知的特性,它允许您使用 Mixins 来创建可重用的小对象。您可以使用多重继承将这些对象组合成更大的对象(类不允许使用多重继承,但混合类允许使用混合类——这类似于具有相关实现的接口)。

有关 TypeScript 混合文件的更多信息

我认为你可以使用这个技术在你的游戏中的许多类之间共享共同的组件,并且在你的游戏中从一个类中重复使用许多这样的组件:

下面是一个快速混合的演示... 首先,你想要混合的口味:

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];
}
});
});
}

然后你可以创建一个混合风味的多重继承类:

class Being implements CanEat, CanSleep {
eat: () => void;
sleep: () => void;
}
applyMixins (Being, [CanEat, CanSleep]);

注意,这个类中没有实际的实现——只是足以使它通过“接口”的要求。但是当我们使用这个类时,一切都很正常。

var being = new Being();


// Zzzzzzz...
being.sleep();

我建议使用这里描述的新的混合方法: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/

这种方法比 Fenton 描述的“ applicyMixins”方法更好,因为自动编译器将帮助您并显示来自基类和第2继承类的所有方法/属性。

这种方法可以在 运输署游乐场地上检查。

以下是实施方案:

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 支持 室内设计师,使用这个特性再加上一个名为 排字稿-混合的小库,你可以使用 Mixin 只需几行代码就可以得到多重继承

// The following line is only for intellisense to work
interface Shopperholic extends Buyer, Transportable {}


class Shopperholic {
// The following line is where we "extend" from other 2 classes
@use( Buyer, Transportable ) this
price = 2000;
}

我认为有一种更好的方法,它支持 固体型式安全型式安全型式安全型式和可伸缩性。

首先声明要在目标类上实现的接口:

interface IBar {
doBarThings(): void;
}


interface IBazz {
doBazzThings(): void;
}


class Foo implements IBar, IBazz {}

现在我们必须将实现添加到 Foo类中。我们可以使用也实现这些接口的类 Mixin:

class Base {}


type Constructor<I = Base> = new (...args: any[]) => I;


function Bar<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBar {
public doBarThings() {
console.log("Do bar!");
}
};
}


function Bazz<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBazz {
public doBazzThings() {
console.log("Do bazz!");
}
};
}

使用 Mixin 类扩展 Foo类:

class Foo extends Bar(Bazz()) implements IBar, IBazz {
public doBarThings() {
super.doBarThings();
console.log("Override mixin");
}
}


const foo = new Foo();
foo.doBazzThings(); // Do bazz!
foo.doBarThings(); // Do bar! // Override mixin

我找到了一个最新的无与伦比的解决方案: https://www.npmjs.com/package/ts-mixer

不客气

一个非常古怪的解决方案是循环遍历您希望从将函数逐个添加到新的父类中继承的类

class ChildA {
public static x = 5
}


class ChildB {
public static y = 6
}


class Parent {}


for (const property in ChildA) {
Parent[property] = ChildA[property]
}
for (const property in ChildB) {
Parent[property] = ChildB[property]
}




Parent.x
// 5
Parent.y
// 6

所有属性的 ChildAChildB现在可以访问从 Parent类,但是他们将不会被认可,这意味着你将收到警告,如 Property 'x' does not exist on 'typeof Parent'

这里已经有很多很好的答案了,但是我只是想用一个例子来说明,你可以为正在扩展的类添加额外的功能;

function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}


class Class1 {
doWork() {
console.log('Working');
}
}


class Class2 {
sleep() {
console.log('Sleeping');
}
}


class FatClass implements Class1, Class2 {
doWork: () => void = () => { };
sleep: () => void = () => { };




x: number = 23;
private _z: number = 80;


get z(): number {
return this._z;
}


set z(newZ) {
this._z = newZ;
}


saySomething(y: string) {
console.log(`Just saying ${y}...`);
}
}
applyMixins(FatClass, [Class1, Class2]);




let fatClass = new FatClass();


fatClass.doWork();
fatClass.saySomething("nothing");
console.log(fatClass.x);

在设计模式中有一个原则叫做“重组合轻继承”。它说,不要从类 A 继承类 B,而是将类 A 的实例放在类 B 中作为属性,然后可以在类 B 中使用类 A 的功能。 您可以看到 给你给你的一些示例。

在我的情况下,我使用 连锁遗传。 也许对某些人来说,这种方式会有所帮助:

class Sprite {
x: number;
y: number;


constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}


class Plane extends Sprite {
fly(): string {
return 'I can Fly!'
}
}


class Enemy {
isEnemy = true;
}


class Player {
isPlayer = true;
}


// You can create factory functions to create new instances
const enemyPlane = Object.assign(new Plane(1, 1), new Enemy());
const playerPlane = Object.assign(new Plane(2, 2), new Player());

此外,我推荐阅读 Eric Elliott 关于 js 继承的文章:

  1. 原型 OO 的心灵: 连续性继承
  2. 三种不同的原型遗传

找到了一个办法:

export interface IsA {
aWork(): void
}


export interface IsB {
   

}


export class A implements IsA {
aWork() {   }
}


export class B implements IsB {
bWork() {   }
}


export interface IsAB extends A, B {}
export class IsAB {}

然后你就可以了

export class C extends IsAB {}

甚至

const c = new IsAB {}

可以调用动态继承或类工厂。

type ClassConstructor<T> = {
new (...args: any[]): T;
};


interface IA {
a: string;
}


interface IB {
b: string;
}


interface IAB extends IA, IB {}


class EmptyClass {}


function GetA<T>(t: ClassConstructor<T> = EmptyClass as any) {
class A extends (t as any) implements IA {
a = 'Default value a';
}
return A as unknown as ClassConstructor<IA & T>;
}


function GetB<T>(t: ClassConstructor<T> = EmptyClass as any) {
class B extends (t as any) implements IB {
b = 'Default value b';
}
return B as unknown as ClassConstructor<IB & T>;
}




class C extends GetA<IB>(GetB()) implements IAB {}