시간을 절약하고 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 피드백 / 조언이 여기에 필요합니다. 가능해야하지만 한 번에 하나만 가능하기 때문에 여러 확장으로 확장 할 수 없습니다. 다른 해결책을 찾지 못했습니다.
답변
Mixins를 사용하여 재사용 가능한 작은 개체를 만들 수있는 TypeScript에는 약간 알려진 기능이 있습니다. 다중 상속을 사용하여이를 더 큰 객체로 구성 할 수 있습니다 (클래스에는 다중 상속이 허용되지 않지만 믹스 인은 허용됩니다.
이 기술을 사용하여 게임의 여러 클래스간에 공통 구성 요소를 공유하고 게임의 단일 클래스에서 이러한 구성 요소를 많이 재사용 할 수 있습니다.
다음은 간단한 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'