polimorfismo interfaz herencia typescript

interfaz - Declaración de método abstracto en TypeScript



polimorfismo en typescript (5)

Creo que usar una combinación de interfaces y clases base podría funcionar para usted. Impondrá requisitos de comportamiento en el momento de la compilación (la publicación rq_ "debajo" se refiere a una publicación anterior, que no es esta).

La interfaz establece la API de comportamiento que no cumple la clase base. No podrá establecer métodos de clase base para llamar a métodos definidos en la interfaz (porque no podrá implementar esa interfaz en la clase base sin tener que definir esos comportamientos). Tal vez alguien pueda inventar un truco seguro para permitir la llamada de los métodos de interfaz en el padre.

Debes recordar ampliar e implementar en la clase que instanciarás. Satisface las preocupaciones sobre la definición de código de tiempo de ejecución y falla. Tampoco podrá llamar a los métodos que podrían vomitar si no ha implementado la interfaz (como si intenta crear una instancia de la clase Animal). Intenté que la interfaz extendiera el BaseAnimal a continuación, pero ocultó el constructor y el campo ''nombre'' de BaseAnima de Snake. Si hubiera podido hacer eso, el uso de un módulo y las exportaciones podrían haber evitado la creación de instancias directas accidentales de la clase BaseAnimal.

Pega esto aquí para ver si te funciona: 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);

Me encontré con la respuesta de FristvanCampen como se vincula a continuación. Él dice que las clases abstractas son un antipatrón y sugiere que una instancia crea clases ''abstractas'' utilizando una instancia inyectada de una clase implementadora. Esto es justo, pero hay argumentos en contra. Lea usted mismo: https://typescript.codeplex.com/discussions/449920

Parte 2: tuve otro caso en el que quería una clase abstracta, pero se me impidió utilizar mi solución anterior, porque los métodos definidos en la "clase abstracta" necesitaban referirse a los métodos definidos en la interfaz correspondiente. Entonces, utilizo el consejo de FristvanCampen, más o menos. Tengo la clase "abstracta" incompleta, con implementaciones de métodos. Tengo la interfaz con los métodos no implementados; esta interfaz amplía la clase "abstracta". Luego tengo una clase que amplía la primera e implementa la segunda (debe extenderse tanto porque el superconstructor es inaccesible de lo contrario). Vea la muestra (no ejecutable) a continuación:

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

y

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

Estoy intentando descubrir cómo definir correctamente los métodos abstractos en TypeScript:

Usando el ejemplo de herencia original:

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

Me gustaría saber cómo definir correctamente el método makeSound, por lo que se tipea y se puede descartar.

Además, no estoy seguro de cómo definir correctamente protected métodos protected : parece ser una palabra clave, pero no tiene ningún efecto y el código no se compilará.


La propiedad del name está marcada como protected . Esto fue agregado en TypeScript 1.3 y ahora está firmemente establecido.

El método makeSound está marcado como abstract , al igual que la clase. No puedes instanciar directamente un Animal ahora, porque es abstracto. Esto es parte de TypeScript 1.6 , que ahora está oficialmente en vivo.

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

La vieja manera de imitar un método abstracto era arrojar un error si alguien lo usaba. Ya no deberías necesitar hacer esto una vez que TypeScript 1.6 llegue a tu proyecto:

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


Si lleva la respuesta de Erics un poco más allá, puede crear una implementación bastante decente de clases abstractas, con soporte completo para el polimorfismo y la capacidad de llamar a los métodos implementados desde la clase base. Comencemos con el código:

/** * 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"

Como la clase Animal requiere una implementación de IAnimal , es imposible construir un objeto de tipo Animal sin tener una implementación válida de los métodos abstractos. Tenga en cuenta que para que el polimorfismo funcione, debe pasar por instancias de IAnimal , no Animal . P.ej:

//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(); }

La principal diferencia aquí con la respuesta de Erics es que la clase base "abstracta" requiere una implementación de la interfaz y, por lo tanto, no puede ser instanciada por sí misma.


Uso para lanzar una excepción en la clase base.

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

Luego debes implementar en la subclase. Los inconvenientes son que no hay error de compilación, sino tiempo de ejecución. La ventaja es que puedes llamar a este método desde la súper clase, suponiendo que funcione :)

HTH!

Milton


¡No no no! No intente crear sus propias clases y métodos "abstractos" cuando el idioma no sea compatible con esa característica; lo mismo ocurre con cualquier función de idioma que desee que sea compatible con un idioma determinado. No hay una forma correcta de implementar métodos abstractos en TypeScript. Simplemente estructure su código con las convenciones de denominación de modo que ciertas clases nunca se creen directamente, pero sin aplicar explícitamente esta prohibición.

Además, el ejemplo anterior solo proporcionará esta aplicación en tiempo de ejecución, NO en tiempo de compilación, como es de esperar en Java / C #.