[typescript] TypeScript에서 추상 메서드 선언

TypeScript에서 추상 메서드를 올바르게 정의하는 방법을 찾으려고합니다.

원래 상속 예제 사용 :

class Animal {
    constructor(public name) { }
    makeSound(input : string) : string;
    move(meters) {
        alert(this.name + " moved " + meters + "m.");
    }
}

class Snake extends Animal {
    constructor(name) { super(name); }
    makeSound(input : string) : string {
        return "sssss"+input;
    }
    move() {
        alert("Slithering...");
        super.move(5);
    }
}

메소드 makeSound를 올바르게 정의하는 방법을 알고 싶습니다. 그래서 입력하고 덮어 쓸 수 있습니다.

또한 올바른 protected방법 을 정의하는 방법을 잘 모르겠습니다 . 키워드 인 것처럼 보이지만 효과가 없으며 코드가 컴파일되지 않습니다.



답변

name속성은 다음과 같이 표시된다 protected. 이것은 TypeScript 1.3에 추가되었으며 이제 확실하게 설정되었습니다.

makeSound방법으로 표시된 abstract클래스처럼. Animal지금은 추상적이기 때문에 직접 인스턴스화 할 수 없습니다 . 이것은 TypeScript 1.6의 일부 이며 현재 공식적으로 제공됩니다.

abstract class Animal {
    constructor(protected name: string) { }

    abstract makeSound(input : string) : string;

    move(meters) {
        alert(this.name + " moved " + meters + "m.");
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }

    makeSound(input : string) : string {
        return "sssss"+input;
    }

    move() {
        alert("Slithering...");
        super.move(5);
    }
}

추상적 인 방법을 모방 한 오래된 방법은 누군가가 그것을 사용하면 오류를 던졌습니다. TypeScript 1.6이 프로젝트에 도달하면 더 이상이 작업을 수행 할 필요가 없습니다.

class Animal {
    constructor(public name) { }
    makeSound(input : string) : string {
        throw new Error('This method is abstract');
    }
    move(meters) {
        alert(this.name + " moved " + meters + "m.");
    }
}

class Snake extends Animal {
    constructor(name) { super(name); }
    makeSound(input : string) : string {
        return "sssss"+input;
    }
    move() {
        alert("Slithering...");
        super.move(5);
    }
}


답변

Erics가 조금 더 대답하면 다형성을 완벽하게 지원하고 기본 클래스에서 구현 된 메소드를 호출 할 수있는 추상 클래스의 꽤 괜찮은 구현을 실제로 만들 수 있습니다. 코드로 시작하자 :

/**
 * The interface defines all abstract methods and extends the concrete base class
 */
interface IAnimal extends Animal {
    speak() : void;
}

/**
 * The abstract base class only defines concrete methods & properties.
 */
class Animal {

    private _impl : IAnimal;

    public name : string;

    /**
     * Here comes the clever part: by letting the constructor take an 
     * implementation of IAnimal as argument Animal cannot be instantiated
     * without a valid implementation of the abstract methods.
     */
    constructor(impl : IAnimal, name : string) {
        this.name = name;
        this._impl = impl;

        // The `impl` object can be used to delegate functionality to the
        // implementation class.
        console.log(this.name + " is born!");
        this._impl.speak();
    }
}

class Dog extends Animal implements IAnimal {
    constructor(name : string) {
        // The child class simply passes itself to Animal
        super(this, name);
    }

    public speak() {
        console.log("bark");
    }
}

var dog = new Dog("Bob");
dog.speak(); //logs "bark"
console.log(dog instanceof Dog); //true
console.log(dog instanceof Animal); //true
console.log(dog.name); //"Bob"

Animal클래스는 구현이 필요하기 때문에 추상 메소드의 유효한 구현 없이는 IAnimal유형의 오브젝트를 구성 할 수 Animal없습니다. 다형성이 작동하려면가 IAnimal아닌 인스턴스를 전달해야합니다 Animal. 예 :

//This works
function letTheIAnimalSpeak(animal: IAnimal) {
    console.log(animal.name + " says:");
    animal.speak();
}
//This doesn't ("The property 'speak' does not exist on value of type 'Animal')
function letTheAnimalSpeak(animal: Animal) {
    console.log(animal.name + " says:");
    animal.speak();
}

Erics의 대답과의 주요 차이점은 “추상적 인”기본 클래스 는 인터페이스의 구현이 필요 하므로 자체적으로 인스턴스화 할 수 없다는 것입니다.


답변

인터페이스와 기본 클래스의 조합을 사용하면 효과가 있다고 생각합니다. 컴파일 타임에 행동 요구 사항을 시행합니다 (rq_ post “아래”는 위의 게시물을 의미합니다).

인터페이스는 기본 클래스가 충족하지 않는 동작 API를 설정합니다. 인터페이스에 정의 된 메소드를 호출하도록 기본 클래스 메소드를 설정할 수 없습니다 (이러한 동작을 정의하지 않고 기본 클래스에서 해당 인터페이스를 구현할 수 없기 때문에). 어쩌면 누군가 부모의 인터페이스 메소드 호출을 허용 하는 안전한 트릭을 만들 수 있습니다.

인스턴스화 할 클래스에서 확장하고 구현해야합니다. 런타임 실패 코드 정의에 대한 우려를 만족시킵니다. 또한 인터페이스를 구현하지 않은 경우 (예 : Animal 클래스를 인스턴스화하려는 경우) 발생하는 메소드를 호출 할 수도 없습니다. 인터페이스를 아래 BaseAnimal로 확장하려고 시도했지만 Snake에서 BaseAnimal의 생성자 및 ‘name’필드를 숨겼습니다. 내가 그렇게 할 수 있다면, 모듈과 내보내기를 사용하면 우연히 BaseAnimal 클래스를 직접 인스턴스화하지 못하게 될 수 있습니다.

여기에 붙여 넣어 자신에게 적합한 지 확인하십시오. http://www.typescriptlang.org/Playground/

// The behavioral interface also needs to extend base for substitutability
interface AbstractAnimal extends BaseAnimal {
    // encapsulates animal behaviors that must be implemented
    makeSound(input : string): string;
}

class BaseAnimal {
    constructor(public name) { }

    move(meters) {
        alert(this.name + " moved " + meters + "m.");
    }
}

// If concrete class doesn't extend both, it cannot use super methods.
class Snake extends BaseAnimal implements AbstractAnimal {
    constructor(name) { super(name); }
    makeSound(input : string): string {
        var utterance = "sssss"+input;
        alert(utterance);
        return utterance;
    }
    move() {
        alert("Slithering...");
        super.move(5);
    }
}

var longMover = new Snake("windy man");

longMover.makeSound("...am I nothing?");
longMover.move();

var fulture = new BaseAnimal("bob fossil");
// compile error on makeSound() because it is not defined.
// fulture.makeSound("you know, like a...")
fulture.move(1);

아래 링크 된 FristvanCampen의 답변을 발견했습니다. 그는 추상 클래스는 반 패턴이며, 하나는 구현 클래스의 주입 된 인스턴스를 사용하여 기본 ‘추상’클래스를 인스턴스화 할 것을 제안합니다. 이것은 공정하지만 반대 주장이 있습니다. 스스로 읽어보십시오 :
https://typescript.codeplex.com/discussions/449920

2 부 : 추상 클래스를 원했던 또 다른 경우가 있었지만 “추상 클래스”의 정의 된 메소드가 일치하는 인터페이스에 정의 된 메소드를 참조해야하기 때문에 위의 솔루션을 사용하지 못했습니다. 그래서 FristvanCampen의 조언을 도구로 사용합니다. 메소드 구현과 함께 불완전한 “추상”클래스가 있습니다. 구현되지 않은 메소드와의 인터페이스가 있습니다. 이 인터페이스는 “추상”클래스를 확장합니다. 그런 다음 첫 번째를 확장하고 두 번째를 구현하는 클래스가 있습니다 (그렇지 않으면 수퍼 생성자가 액세스 할 수 없으므로 둘 다 확장해야합니다). 아래의 (실행 불가능한) 샘플을 참조하십시오.

export class OntologyConceptFilter extends FilterWidget.FilterWidget<ConceptGraph.Node, ConceptGraph.Link> implements FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link> {

    subMenuTitle = "Ontologies Rendered"; // overload or overshadow?

    constructor(
        public conceptGraph: ConceptGraph.ConceptGraph,
        graphView: PathToRoot.ConceptPathsToRoot,
        implementation: FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link>
        ){
        super(graphView);
        this.implementation = this;
    }
}

export class FilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> {

    public implementation: IFilterWidget<N, L>

    filterContainer: JQuery;

    public subMenuTitle : string; // Given value in children

    constructor(
        public graphView: GraphView.GraphView<N, L>
        ){

    }

    doStuff(node: N){
        this.implementation.generateStuff(thing);
    }

}

export interface IFilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> extends FilterWidget<N, L> {

    generateStuff(node: N): string;

}


답변

기본 클래스에서 예외를 던지는 데 사용합니다.

protected abstractMethod() {
    throw new Error("abstractMethod not implemented");
}

그런 다음 하위 클래스에서 구현해야합니다. 단점은 빌드 오류가 없지만 런타임이라는 것입니다. 장점은 작동한다고 가정하고 수퍼 클래스 에서이 메소드를 호출 할 수 있다는 것입니다. 🙂

HTH!

밀턴


답변

아니, 아니! 언어가 해당 기능을 지원하지 않을 때 자신 만의 ‘추상’클래스와 메소드를 만들려고 시도하지 마십시오. 주어진 언어가 지원되기를 원하는 언어 기능도 마찬가지입니다. TypeScript에서 추상 메서드를 구현하는 올바른 방법은 없습니다. 특정 클래스가 직접 인스턴스화되지는 않지만 명시 적 으로이 금지 사항을 적용하지 않도록 이름 지정 규칙으로 코드를 구성하십시오.

또한 위의 예제는 Java / C #에서 예상 한 것처럼 컴파일 타임이 아닌 런타임에만이 적용을 제공합니다.


답변