[javascript] Typescript : 두 클래스를 확장하는 방법은 무엇입니까?

시간을 절약하고 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 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 피드백 / 조언이 여기에 필요합니다. 가능해야하지만 한 번에 하나만 가능하기 때문에 여러 확장으로 확장 할 수 없습니다. 다른 해결책을 찾지 못했습니다.



답변

Mixins를 사용하여 재사용 가능한 작은 개체를 만들 수있는 TypeScript에는 약간 알려진 기능이 있습니다. 다중 상속을 사용하여이를 더 큰 객체로 구성 할 수 있습니다 (클래스에는 다중 상속이 허용되지 않지만 믹스 인은 허용됩니다.

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 풍미에서 다중 상속을 사용하여 클래스를 만들 수 있습니다.

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이 설명한 “applyMixins”접근 방식보다 낫습니다. 자동 컴파일러가 기본 및 두 번째 상속 클래스의 모든 메서드 / 속성을 표시하고 도움을주기 때문입니다.

이 접근 방식은 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 등의 공통 수퍼 클래스를 파생시킬 수 있습니까? hirarchy 유형에서 좋은 자리를 찾을 수 있다면 이것은 좋은 선택이 될 것입니다. 그러나이 클래스를 임의의 지점에 삽입하는 것은 권장하지 않습니다. 상속은 “Is a-관계”를 표현합니다. 예를 들어 개는 동물이고 텍스처는이 클래스의 인스턴스입니다. 이것이 실제로 코드의 객체 간의 관계를 모델링하는지 스스로에게 물어봐야 할 것입니다. 논리적 상속 트리는 매우 중요합니다.

  • 추가 클래스가 유형 계층 구조에 논리적으로 맞지 않는 경우 집계를 사용할 수 있습니다. 즉,이 클래스 유형의 인스턴스 변수를 Sprite, Texture, Layer, …의 공통 수퍼 클래스에 추가하면 모든 하위 클래스에서 getter / setter를 사용하여 변수에 액세스 할 수 있습니다. 이것은 “관계 있음”을 모델링합니다.

  • 클래스를 인터페이스로 변환 할 수도 있습니다. 그런 다음 모든 클래스로 인터페이스를 확장 할 수 있지만 각 클래스에서 메서드를 올바르게 구현해야합니다. 이것은 일부 코드 중복을 의미하지만이 경우에는 많지 않습니다.

어떤 접근 방식을 가장 좋아하는지 스스로 결정해야합니다. 개인적으로 클래스를 인터페이스로 변환하는 것이 좋습니다.

한 가지 팁 : Typescript는 getter 및 setter를위한 구문 설탕 인 속성을 제공합니다. http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/를 살펴보십시오.


답변

TypeScript는 데코레이터를 지원하며 해당 기능과 typescript-mix 라는 작은 라이브러리 를 사용하면 믹스 인 을 사용하여 단 몇 줄로 여러 상속을 가질 수 있습니다.

// 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클래스에 구현을 추가해야합니다 . 다음 인터페이스도 구현하는 클래스 믹스 인을 사용할 수 있습니다.

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!");
    }
  };
}

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


답변

매우 해키 한 해결책은 새 부모 클래스에 함수를 하나씩 추가하여 상속하려는 클래스를 반복하는 것입니다.

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

의 모든 속성 ChildA과는 ChildB이제 액세스 할 수 있습니다 Parent하지만 그들은 당신과 같은 경고를 받게됩니다 것을 의미 인식되지 않습니다, 클래스Property 'x' does not exist on 'typeof Parent'


답변

디자인 패턴에는 “상속보다 구성을 선호”라는 원칙이 있습니다. 클래스 A에서 클래스 B를 상속하는 대신 클래스 B에 클래스 A의 인스턴스를 속성으로 넣은 다음 클래스 B 내부에 클래스 A의 기능을 사용할 수 있습니다 . 여기여기 에서 몇 가지 예를 볼 수 있습니다 .